//
//  TravelAlbumCameraImportViewController.swift
//  suixinkan
//

import SnapKit
import UIKit

/// 旅拍相册相机历史照片选择导入页。
final class TravelAlbumCameraImportViewController: UIViewController {
    private enum Layout {
        static let columns: CGFloat = 3
        static let spacing: CGFloat = 2
        static let inset: CGFloat = 2
        static let headerHeight: CGFloat = 44
    }

    private let viewModel: TravelAlbumCameraImportViewModel
    var onImportFinished: ((Int, Int, [String]) -> Void)?
    var onImportCancelled: (() -> Void)?

    private var collectionView: UICollectionView!
    private var sections: [TravelAlbumCameraImportSection] = []
    private var lastCollectionWidth: CGFloat = 0

    private lazy var importBarButton = UIBarButtonItem(
        title: "导入 (0)",
        style: .done,
        target: self,
        action: #selector(importTapped)
    )
    private lazy var helpBarButton = UIBarButtonItem(
        image: UIImage(systemName: "questionmark.circle"),
        style: .plain,
        target: self,
        action: #selector(helpTapped)
    )
    private let activityIndicator = UIActivityIndicatorView(style: .large)
    private let messageLabel = UILabel()
    private let helpButton = UIButton(type: .system)

    /// 初始化旅拍相册相机历史照片选择导入页。
    init(viewModel: TravelAlbumCameraImportViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        title = "选择照片"
        view.backgroundColor = .white
        navigationItem.leftBarButtonItem = UIBarButtonItem(
            title: "取消",
            style: .plain,
            target: self,
            action: #selector(cancelTapped)
        )
        navigationItem.rightBarButtonItem = importBarButton
        importBarButton.isEnabled = false
        setupUI()
        setupConstraints()
        bindViewModel()
        viewModel.loadPhotos()
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        let width = collectionView.bounds.width
        guard width > 0, width != lastCollectionWidth else { return }
        lastCollectionWidth = width
        collectionView.collectionViewLayout.invalidateLayout()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        navigationController?.presentationController?.delegate = self
    }

    private func setupUI() {
        let layout = UICollectionViewFlowLayout()
        layout.minimumLineSpacing = Layout.spacing
        layout.minimumInteritemSpacing = Layout.spacing
        layout.sectionInset = UIEdgeInsets(
            top: Layout.inset,
            left: Layout.inset,
            bottom: Layout.inset,
            right: Layout.inset
        )
        layout.headerReferenceSize = CGSize(width: 0, height: Layout.headerHeight)

        collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.backgroundColor = .white
        collectionView.allowsMultipleSelection = true
        collectionView.dataSource = self
        collectionView.delegate = self
        collectionView.register(
            TravelAlbumCameraImportPhotoCell.self,
            forCellWithReuseIdentifier: TravelAlbumCameraImportPhotoCell.reuseIdentifier
        )
        collectionView.register(
            TravelAlbumCameraImportSectionHeaderView.self,
            forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
            withReuseIdentifier: TravelAlbumCameraImportSectionHeaderView.reuseIdentifier
        )

        activityIndicator.hidesWhenStopped = true
        messageLabel.numberOfLines = 0
        messageLabel.textAlignment = .center
        messageLabel.textColor = AppColor.textSecondary
        messageLabel.font = .systemFont(ofSize: 15)
        messageLabel.isHidden = true

        helpButton.setTitle("如何切换到 MTP 模式", for: .normal)
        helpButton.titleLabel?.font = .systemFont(ofSize: 15, weight: .medium)
        helpButton.backgroundColor = AppColor.primary.withAlphaComponent(0.10)
        helpButton.setTitleColor(AppColor.primary, for: .normal)
        helpButton.layer.cornerRadius = 9
        helpButton.isHidden = true
        helpButton.addTarget(self, action: #selector(helpTapped), for: .touchUpInside)

        view.addSubview(collectionView)
        view.addSubview(activityIndicator)
        view.addSubview(messageLabel)
        view.addSubview(helpButton)
    }

    private func setupConstraints() {
        collectionView.snp.makeConstraints { make in
            make.edges.equalTo(view.safeAreaLayoutGuide)
        }
        activityIndicator.snp.makeConstraints { make in
            make.center.equalToSuperview()
        }
        messageLabel.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.centerY.equalToSuperview().offset(-28)
            make.leading.greaterThanOrEqualToSuperview().offset(32)
            make.trailing.lessThanOrEqualToSuperview().offset(-32)
        }
        helpButton.snp.makeConstraints { make in
            make.top.equalTo(messageLabel.snp.bottom).offset(20)
            make.centerX.equalToSuperview()
            make.height.equalTo(38)
            make.width.greaterThanOrEqualTo(160)
        }
    }

    private func bindViewModel() {
        viewModel.onStateUpdated = { [weak self] state in
            self?.render(state)
        }
        viewModel.onSelectionUpdated = { [weak self] count in
            self?.updateImportButton(count: count)
        }
        viewModel.onSonyMTPHelpVisibilityChanged = { [weak self] show in
            self?.updateHelpVisibility(show)
        }
    }

    private func render(_ state: TravelAlbumCameraImportState) {
        switch state {
        case .idle, .loading:
            isModalInPresentation = false
            collectionView.isHidden = true
            messageLabel.isHidden = true
            helpButton.isHidden = true
            importBarButton.isEnabled = false
            navigationItem.leftBarButtonItem?.isEnabled = true
            activityIndicator.startAnimating()

        case .loaded(let loadedSections):
            isModalInPresentation = false
            activityIndicator.stopAnimating()
            collectionView.isHidden = false
            messageLabel.isHidden = true
            helpButton.isHidden = true
            navigationItem.leftBarButtonItem?.isEnabled = true
            sections = loadedSections
            collectionView.reloadData()

        case .importing(let current, let total):
            isModalInPresentation = true
            collectionView.isHidden = true
            messageLabel.isHidden = false
            messageLabel.text = "正在导入 \(current)/\(total)..."
            helpButton.isHidden = true
            importBarButton.isEnabled = false
            navigationItem.leftBarButtonItem?.isEnabled = false
            activityIndicator.startAnimating()

        case .finished(let importedCount, let failedCount, let importedPhotoIds):
            isModalInPresentation = false
            activityIndicator.stopAnimating()
            dismiss(animated: true) { [weak self] in
                self?.onImportFinished?(importedCount, failedCount, importedPhotoIds)
            }

        case .failed(let message):
            isModalInPresentation = false
            activityIndicator.stopAnimating()
            collectionView.isHidden = true
            messageLabel.isHidden = false
            messageLabel.text = message
            importBarButton.isEnabled = false
            navigationItem.leftBarButtonItem?.isEnabled = true
            helpButton.isHidden = !viewModel.shouldShowSonyMTPHelp
        }
    }

    private func updateHelpVisibility(_ show: Bool) {
        navigationItem.rightBarButtonItems = show ? [importBarButton, helpBarButton] : [importBarButton]
        if case .failed = viewModel.state {
            helpButton.isHidden = !show
        }
    }

    private func updateImportButton(count: Int) {
        importBarButton.title = "导入 (\(count))"
        importBarButton.isEnabled = count > 0
    }

    private func thumbnailPixelSize() -> Int {
        guard let flow = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
            return 300
        }
        return Int(itemSize(layout: flow).width * UIScreen.main.scale)
    }

    private func itemSize(layout: UICollectionViewFlowLayout) -> CGSize {
        let totalSpacing = layout.sectionInset.left
            + layout.sectionInset.right
            + layout.minimumInteritemSpacing * (Layout.columns - 1)
        let width = floor((collectionView.bounds.width - totalSpacing) / Layout.columns)
        return CGSize(width: width, height: width + 28)
    }

    private func applyHeader(_ header: TravelAlbumCameraImportSectionHeaderView, at sectionIndex: Int) {
        let section = sections[sectionIndex]
        header.apply(
            title: section.title,
            count: section.photos.count,
            importableCount: viewModel.importablePhotoCount(in: section),
            importedCount: viewModel.importedPhotoCount(in: section),
            hasImportablePhotos: viewModel.hasImportablePhotos(in: section),
            fullySelected: viewModel.isSectionFullySelected(section),
            partiallySelected: viewModel.isSectionPartiallySelected(section)
        )
        header.onToggle = { [weak self] in
            self?.toggleSection(at: sectionIndex)
        }
    }

    private func refreshVisibleHeader(at sectionIndex: Int) {
        let indexPath = IndexPath(item: 0, section: sectionIndex)
        guard let header = collectionView.supplementaryView(
            forElementKind: UICollectionView.elementKindSectionHeader,
            at: indexPath
        ) as? TravelAlbumCameraImportSectionHeaderView else {
            return
        }
        applyHeader(header, at: sectionIndex)
    }

    private func reloadItemsWithoutAnimation(_ indexPaths: [IndexPath]) {
        UIView.performWithoutAnimation {
            collectionView.reloadItems(at: indexPaths)
            collectionView.layoutIfNeeded()
        }
    }

    private func toggleSection(at sectionIndex: Int) {
        guard sections.indices.contains(sectionIndex) else { return }
        viewModel.toggleSection(sections[sectionIndex])
        let indexPaths = sections[sectionIndex].photos.indices.map {
            IndexPath(item: $0, section: sectionIndex)
        }
        reloadItemsWithoutAnimation(indexPaths)
        refreshVisibleHeader(at: sectionIndex)
    }

    @objc private func cancelTapped() {
        onImportCancelled?()
        dismiss(animated: true)
    }

    @objc private func importTapped() {
        viewModel.importSelected()
    }

    @objc private func helpTapped() {
        guard let url = WiredCameraTransferHelpDocument.bundledURL() else { return }
        navigationController?.pushViewController(
            AgreementWebViewController(title: WiredCameraTransferHelpDocument.pageTitle, url: url),
            animated: true
        )
    }
}

extension TravelAlbumCameraImportViewController: UICollectionViewDataSource {
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        sections.count
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        sections[section].photos.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(
            withReuseIdentifier: TravelAlbumCameraImportPhotoCell.reuseIdentifier,
            for: indexPath
        ) as! TravelAlbumCameraImportPhotoCell
        let photo = sections[indexPath.section].photos[indexPath.item]
        cell.apply(
            photo: photo,
            selected: viewModel.isPhotoSelected(id: photo.id),
            alreadyImported: viewModel.isPhotoAlreadyInAlbum(id: photo.id),
            maxPixelSize: thumbnailPixelSize()
        ) { [weak self] photo, maxPixelSize in
            await self?.viewModel.thumbnailData(for: photo, maxPixelSize: maxPixelSize)
        }
        return cell
    }

    func collectionView(
        _ collectionView: UICollectionView,
        viewForSupplementaryElementOfKind kind: String,
        at indexPath: IndexPath
    ) -> UICollectionReusableView {
        guard kind == UICollectionView.elementKindSectionHeader else {
            return UICollectionReusableView()
        }
        let header = collectionView.dequeueReusableSupplementaryView(
            ofKind: kind,
            withReuseIdentifier: TravelAlbumCameraImportSectionHeaderView.reuseIdentifier,
            for: indexPath
        ) as! TravelAlbumCameraImportSectionHeaderView
        applyHeader(header, at: indexPath.section)
        return header
    }
}

extension TravelAlbumCameraImportViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let photo = sections[indexPath.section].photos[indexPath.item]
        guard !viewModel.isPhotoAlreadyInAlbum(id: photo.id) else { return }
        viewModel.togglePhoto(id: photo.id)
        reloadItemsWithoutAnimation([indexPath])
        refreshVisibleHeader(at: indexPath.section)
    }

    func collectionView(
        _ collectionView: UICollectionView,
        layout collectionViewLayout: UICollectionViewLayout,
        sizeForItemAt indexPath: IndexPath
    ) -> CGSize {
        guard let flow = collectionViewLayout as? UICollectionViewFlowLayout else {
            return CGSize(width: 120, height: 148)
        }
        return itemSize(layout: flow)
    }
}

extension TravelAlbumCameraImportViewController: UIAdaptivePresentationControllerDelegate {
    func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
        onImportCancelled?()
    }
}

/// 相机历史照片导入 Cell。
private final class TravelAlbumCameraImportPhotoCell: UICollectionViewCell {
    static let reuseIdentifier = "TravelAlbumCameraImportPhotoCell"

    private let imageView = UIImageView()
    private let filenameLabel = UILabel()
    private let checkmarkView = UIImageView()
    private let selectedOverlayView = UIView()
    private let importedOverlayView = UIView()
    private let importedBadgeLabel = UILabel()
    private var representedPhotoId: String?
    private var loadToken = UUID()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        representedPhotoId = nil
        loadToken = UUID()
        imageView.image = nil
        filenameLabel.text = nil
        applySelection(false, alreadyImported: false)
        applyImported(false)
    }

    /// 绑定相机照片与选择状态。
    func apply(
        photo: CameraObject,
        selected: Bool,
        alreadyImported: Bool,
        maxPixelSize: Int,
        thumbnailProvider: @escaping (CameraObject, Int) async -> Data?
    ) {
        representedPhotoId = photo.id
        filenameLabel.text = photo.filename
        setPlaceholderImage()
        applyImported(alreadyImported)
        applySelection(selected, alreadyImported: alreadyImported)

        let token = UUID()
        loadToken = token
        Task { @MainActor in
            guard let data = await thumbnailProvider(photo, maxPixelSize),
                  self.loadToken == token,
                  self.representedPhotoId == photo.id,
                  let image = UIImage(data: data) else {
                return
            }
            self.imageView.image = image
            self.imageView.tintColor = nil
        }
    }

    private func setupUI() {
        contentView.backgroundColor = AppColor.pageBackground
        contentView.clipsToBounds = true
        contentView.layer.cornerRadius = 4

        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        selectedOverlayView.backgroundColor = AppColor.primary.withAlphaComponent(0.25)
        selectedOverlayView.isHidden = true
        importedOverlayView.backgroundColor = UIColor.black.withAlphaComponent(0.35)
        importedOverlayView.isHidden = true

        importedBadgeLabel.text = "已在相册"
        importedBadgeLabel.font = .systemFont(ofSize: 9, weight: .semibold)
        importedBadgeLabel.textColor = .white
        importedBadgeLabel.textAlignment = .center
        importedBadgeLabel.backgroundColor = UIColor.black.withAlphaComponent(0.55)
        importedBadgeLabel.layer.cornerRadius = 4
        importedBadgeLabel.clipsToBounds = true
        importedBadgeLabel.isHidden = true

        checkmarkView.image = UIImage(systemName: "checkmark.circle.fill")
        checkmarkView.tintColor = AppColor.primary
        checkmarkView.isHidden = true

        filenameLabel.font = .systemFont(ofSize: 9, weight: .medium)
        filenameLabel.textColor = AppColor.textSecondary
        filenameLabel.textAlignment = .center
        filenameLabel.numberOfLines = 2

        [imageView, selectedOverlayView, importedOverlayView, importedBadgeLabel, checkmarkView, filenameLabel].forEach {
            contentView.addSubview($0)
        }

        imageView.snp.makeConstraints { make in
            make.top.leading.trailing.equalToSuperview()
            make.height.equalTo(contentView.snp.width)
        }
        selectedOverlayView.snp.makeConstraints { make in
            make.top.leading.trailing.equalToSuperview()
            make.bottom.equalTo(imageView)
        }
        importedOverlayView.snp.makeConstraints { make in
            make.top.leading.trailing.equalToSuperview()
            make.bottom.equalTo(imageView)
        }
        importedBadgeLabel.snp.makeConstraints { make in
            make.center.equalTo(imageView)
            make.height.equalTo(18)
            make.width.greaterThanOrEqualTo(52)
        }
        checkmarkView.snp.makeConstraints { make in
            make.top.trailing.equalToSuperview().inset(4)
            make.size.equalTo(22)
        }
        filenameLabel.snp.makeConstraints { make in
            make.top.equalTo(imageView.snp.bottom).offset(2)
            make.leading.trailing.equalToSuperview().inset(2)
            make.bottom.lessThanOrEqualToSuperview().offset(-2)
        }
    }

    private func setPlaceholderImage() {
        let config = UIImage.SymbolConfiguration(pointSize: 28, weight: .light)
        imageView.image = UIImage(systemName: "photo", withConfiguration: config)
        imageView.tintColor = AppColor.textTertiary
    }

    private func applySelection(_ selected: Bool, alreadyImported: Bool) {
        let showSelection = selected && !alreadyImported
        selectedOverlayView.isHidden = !showSelection
        checkmarkView.isHidden = !showSelection
        contentView.layer.borderWidth = showSelection ? 2 : 0
        contentView.layer.borderColor = showSelection ? AppColor.primary.cgColor : nil
    }

    private func applyImported(_ alreadyImported: Bool) {
        importedOverlayView.isHidden = !alreadyImported
        importedBadgeLabel.isHidden = !alreadyImported
        contentView.alpha = alreadyImported ? 0.55 : 1
    }
}

/// 相机历史照片导入分组 Header。
private final class TravelAlbumCameraImportSectionHeaderView: UICollectionReusableView {
    static let reuseIdentifier = "TravelAlbumCameraImportSectionHeaderView"

    var onToggle: (() -> Void)?

    private let checkButton = UIButton(type: .system)
    private let titleLabel = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    /// 绑定分组信息。
    func apply(
        title: String,
        count: Int,
        importableCount: Int,
        importedCount: Int,
        hasImportablePhotos: Bool,
        fullySelected: Bool,
        partiallySelected: Bool
    ) {
        titleLabel.text = importedCount > 0 ? "\(title) · 可导入 \(importableCount) / 共 \(count) 张" : "\(title) · \(count) 张"
        checkButton.isEnabled = hasImportablePhotos
        let symbolName: String
        if !hasImportablePhotos {
            symbolName = "circle"
        } else if fullySelected {
            symbolName = "checkmark.circle.fill"
        } else if partiallySelected {
            symbolName = "minus.circle.fill"
        } else {
            symbolName = "circle"
        }
        checkButton.setImage(UIImage(systemName: symbolName), for: .normal)
        checkButton.tintColor = hasImportablePhotos && (fullySelected || partiallySelected) ? AppColor.primary : AppColor.textTertiary
    }

    private func setupUI() {
        backgroundColor = .white
        checkButton.addTarget(self, action: #selector(toggleTapped), for: .touchUpInside)
        titleLabel.font = .systemFont(ofSize: 15, weight: .semibold)
        titleLabel.textColor = AppColor.textPrimary

        addSubview(checkButton)
        addSubview(titleLabel)
        checkButton.snp.makeConstraints { make in
            make.leading.equalToSuperview().offset(12)
            make.centerY.equalToSuperview()
            make.size.equalTo(28)
        }
        titleLabel.snp.makeConstraints { make in
            make.leading.equalTo(checkButton.snp.trailing).offset(8)
            make.trailing.equalToSuperview().offset(-12)
            make.centerY.equalToSuperview()
        }
    }

    @objc private func toggleTapped() {
        onToggle?()
    }
}
