//
//  CloudDriveListViewController.swift
//  suixinkan
//

import PhotosUI
import SnapKit
import UIKit
import UniformTypeIdentifiers

/// 相册云盘列表页，严格对齐 Android `CloudStorageListScreen`。
final class CloudDriveListViewController: BaseViewController {
    private let viewModel = CloudDriveListViewModel()
    private let api: any CloudDriveServing
    private let transferManager: CloudTransferManager

    private let navigationDivider = UIView()
    private let pathScrollView = UIScrollView()
    private let pathStackView = UIStackView()
    private let searchContainer = UIView()
    private let searchIconView = UIImageView(
        image: CloudDriveAsset.image(named: "icon_cloud_storage_file_search", fallbackSystemName: "magnifyingglass")
    )
    private let searchField = UITextField()
    private let sortButton = CloudDriveMenuButton()
    private let filterButton = CloudDriveMenuButton()
    private let displayModeButton = UIButton(type: .system)
    private let contentDivider = UIView()
    private var collectionView: UICollectionView!
    private var dataSource: UICollectionViewDiffableDataSource<Int, CloudFile>!
    private let refreshControl = UIRefreshControl()
    private let loadingMoreIndicator = UIActivityIndicatorView(style: .medium)
    private let uploadButton = UIButton(type: .system)
    private let bottomBar = UIView()

    private var appliedDisplayMode: CloudDriveDisplayMode?
    private var lastInteractionTimes: [String: TimeInterval] = [:]
    private var previousStandardAppearance: UINavigationBarAppearance?
    private var previousScrollEdgeAppearance: UINavigationBarAppearance?
    private var previousCompactAppearance: UINavigationBarAppearance?
    private var previousNavigationTintColor: UIColor?

    /// 初始化相册云盘列表页。
    init(
        api: (any CloudDriveServing)? = nil,
        transferManager: CloudTransferManager? = nil
    ) {
        self.api = api ?? NetworkServices.shared.cloudDriveAPI
        self.transferManager = transferManager ?? .shared
        super.init(nibName: nil, bundle: nil)
    }

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

    override func setupNavigationBar() {
        let titleLabel = UILabel()
        titleLabel.text = "相册云盘"
        titleLabel.textColor = UIColor(hex: 0x333333)
        titleLabel.font = .systemFont(ofSize: 18)
        navigationItem.titleView = titleLabel

        let backButton = UIButton(type: .system)
        backButton.setImage(UIImage(systemName: "chevron.left")?.withConfiguration(UIImage.SymbolConfiguration(pointSize: 20, weight: .medium)), for: .normal)
        backButton.tintColor = .black
        backButton.accessibilityLabel = "返回"
        backButton.addTarget(self, action: #selector(backTapped), for: .touchUpInside)
        backButton.snp.makeConstraints { make in
            make.width.height.equalTo(44)
        }
        navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backButton)

        let transferButton = UIButton(type: .system)
        transferButton.setImage(
            CloudDriveAsset.resizedImage(
                named: "icon_cloud_storage_transit",
                fallbackSystemName: "arrow.up.arrow.down.circle",
                size: CGSize(width: 20, height: 20)
            ),
            for: .normal
        )
        transferButton.imageView?.contentMode = .scaleAspectFit
        transferButton.accessibilityLabel = "传输管理"
        transferButton.addTarget(self, action: #selector(openTransferManager), for: .touchUpInside)
        transferButton.snp.makeConstraints { make in
            make.width.height.equalTo(44)
        }
        navigationItem.rightBarButtonItem = UIBarButtonItem(customView: transferButton)
    }

    override func setupUI() {
        view.backgroundColor = .white
        navigationDivider.backgroundColor = UIColor(hex: 0xEEEEEE)

        pathScrollView.showsHorizontalScrollIndicator = false
        pathScrollView.alwaysBounceHorizontal = false
        pathStackView.axis = .horizontal
        pathStackView.alignment = .center
        pathStackView.spacing = 0
        pathScrollView.addSubview(pathStackView)

        searchContainer.backgroundColor = UIColor(hex: 0xF4F4F4)
        searchContainer.layer.cornerRadius = 4
        searchIconView.contentMode = .scaleAspectFit
        searchField.attributedPlaceholder = NSAttributedString(
            string: "搜索文件名称",
            attributes: [
                .font: UIFont.systemFont(ofSize: 14),
                .foregroundColor: UIColor(hex: 0xB6BECA),
            ]
        )
        searchField.font = .systemFont(ofSize: 14)
        searchField.textColor = UIColor(hex: 0x333333)
        searchField.returnKeyType = .search
        searchField.clearButtonMode = .never
        searchField.delegate = self
        searchContainer.addSubview(searchIconView)
        searchContainer.addSubview(searchField)

        sortButton.addTarget(self, action: #selector(sortTapped), for: .touchUpInside)
        filterButton.addTarget(self, action: #selector(filterTapped), for: .touchUpInside)
        displayModeButton.backgroundColor = .white
        displayModeButton.imageView?.contentMode = .scaleAspectFit
        displayModeButton.accessibilityLabel = "切换展示方式"

        contentDivider.backgroundColor = UIColor(hex: 0xEEEEEE)
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: makeLayout())
        collectionView.backgroundColor = .white
        collectionView.alwaysBounceVertical = true
        collectionView.delegate = self
        collectionView.refreshControl = refreshControl
        collectionView.register(CloudDriveFileCell.self, forCellWithReuseIdentifier: CloudDriveFileCell.reuseIdentifier)
        dataSource = UICollectionViewDiffableDataSource<Int, CloudFile>(collectionView: collectionView) { [weak self] collectionView, indexPath, file in
            guard let self,
                  let cell = collectionView.dequeueReusableCell(
                    withReuseIdentifier: CloudDriveFileCell.reuseIdentifier,
                    for: indexPath
                  ) as? CloudDriveFileCell else {
                return UICollectionViewCell()
            }
            cell.apply(file: file, mode: self.viewModel.displayMode)
            cell.onDetails = { [weak self] in
                guard let self, self.shouldHandleInteraction("details_\(file.id)") else { return }
                self.showControlSheet(for: file)
            }
            return cell
        }

        loadingMoreIndicator.color = AppColor.primary
        collectionView.addSubview(loadingMoreIndicator)

        bottomBar.backgroundColor = .white
        bottomBar.layer.borderColor = UIColor(hex: 0xEEEEEE).cgColor
        bottomBar.layer.borderWidth = 0.5

        uploadButton.setTitle("上传", for: .normal)
        uploadButton.setTitleColor(.white, for: .normal)
        uploadButton.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
        uploadButton.backgroundColor = AppColor.primary
        uploadButton.layer.cornerRadius = 10
        uploadButton.accessibilityLabel = "上传"

        [navigationDivider, pathScrollView, searchContainer, sortButton, filterButton, displayModeButton, contentDivider, collectionView, bottomBar]
            .forEach(view.addSubview)
        bottomBar.addSubview(uploadButton)
    }

    override func setupConstraints() {
        navigationDivider.snp.makeConstraints { make in
            make.top.equalTo(view.safeAreaLayoutGuide)
            make.leading.trailing.equalToSuperview()
            make.height.equalTo(0.5)
        }
        pathScrollView.snp.makeConstraints { make in
            make.top.equalTo(navigationDivider.snp.bottom)
            make.leading.trailing.equalToSuperview().inset(16)
            make.height.equalTo(52)
        }
        pathStackView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
            make.height.equalToSuperview()
        }
        searchContainer.snp.makeConstraints { make in
            make.top.equalTo(pathScrollView.snp.bottom).offset(2)
            make.leading.trailing.equalToSuperview().inset(16)
            make.height.equalTo(36)
        }
        searchIconView.snp.makeConstraints { make in
            make.leading.equalToSuperview().offset(12)
            make.centerY.equalToSuperview()
            make.width.height.equalTo(16)
        }
        searchField.snp.makeConstraints { make in
            make.leading.equalTo(searchIconView.snp.trailing).offset(12)
            make.top.bottom.equalToSuperview()
            make.trailing.equalToSuperview().inset(8)
        }
        sortButton.snp.makeConstraints { make in
            make.top.equalTo(searchContainer.snp.bottom).offset(16)
            make.leading.equalToSuperview().offset(16)
            make.height.equalTo(36)
        }
        filterButton.snp.makeConstraints { make in
            make.top.width.height.equalTo(sortButton)
            make.leading.equalTo(sortButton.snp.trailing).offset(12)
        }
        displayModeButton.snp.makeConstraints { make in
            make.centerY.equalTo(sortButton)
            make.leading.equalTo(filterButton.snp.trailing).offset(12)
            make.trailing.equalToSuperview().inset(6)
            make.width.height.equalTo(35)
        }
        sortButton.snp.makeConstraints { make in
            make.width.equalTo(filterButton)
        }
        contentDivider.snp.makeConstraints { make in
            make.top.equalTo(sortButton.snp.bottom).offset(16)
            make.leading.trailing.equalToSuperview()
            make.height.equalTo(0.5)
        }
        bottomBar.snp.makeConstraints { make in
            make.leading.trailing.bottom.equalToSuperview()
        }
        uploadButton.snp.makeConstraints { make in
            make.top.equalToSuperview().offset(16)
            make.leading.trailing.equalToSuperview().inset(16)
            make.height.equalTo(48)
            make.bottom.equalTo(view.safeAreaLayoutGuide).inset(16)
        }
        collectionView.snp.makeConstraints { make in
            make.top.equalTo(contentDivider.snp.bottom)
            make.leading.trailing.equalToSuperview()
            make.bottom.equalTo(bottomBar.snp.top)
        }
        loadingMoreIndicator.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.bottom.equalTo(collectionView.frameLayoutGuide).inset(12)
        }
    }

    override func bindActions() {
        uploadButton.addTarget(self, action: #selector(uploadTapped), for: .touchUpInside)
        displayModeButton.addTarget(self, action: #selector(displayModeTapped), for: .touchUpInside)
        refreshControl.addTarget(self, action: #selector(refreshTriggered), for: .valueChanged)

        viewModel.onStateChange = { [weak self] in
            Task { @MainActor in self?.applyViewModel() }
        }
        viewModel.onShowMessage = { [weak self] message in
            Task { @MainActor in self?.showToast(message) }
        }
        transferManager.onUploadCompleted = { [weak self] folderId in
            guard let self else { return }
            Task { await self.viewModel.handleUploadCompleted(parentFolderId: folderId, api: self.api) }
        }
        transferManager.onShowMessage = { [weak self] message in
            Task { @MainActor in self?.showToast(message) }
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        Task { await viewModel.refresh(api: api, showLoading: true) }
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        applyCloudNavigationBarAppearance()
        updateInteractivePopGesture()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        restoreNavigationBarAppearance()
        navigationController?.interactivePopGestureRecognizer?.isEnabled = true
    }

    private func applyCloudNavigationBarAppearance() {
        guard let navigationBar = navigationController?.navigationBar else { return }
        previousStandardAppearance = navigationBar.standardAppearance
        previousScrollEdgeAppearance = navigationBar.scrollEdgeAppearance
        previousCompactAppearance = navigationBar.compactAppearance
        previousNavigationTintColor = navigationBar.tintColor
        let appearance = UINavigationBarAppearance()
        appearance.configureWithOpaqueBackground()
        appearance.backgroundColor = .white
        appearance.shadowColor = .clear
        navigationBar.standardAppearance = appearance
        navigationBar.scrollEdgeAppearance = appearance
        navigationBar.compactAppearance = appearance
        navigationBar.tintColor = .black
    }

    private func restoreNavigationBarAppearance() {
        guard let navigationBar = navigationController?.navigationBar else { return }
        if let previousStandardAppearance {
            navigationBar.standardAppearance = previousStandardAppearance
        }
        navigationBar.scrollEdgeAppearance = previousScrollEdgeAppearance
        navigationBar.compactAppearance = previousCompactAppearance
        navigationBar.tintColor = previousNavigationTintColor
    }

    @MainActor
    private func applyViewModel() {
        rebuildBreadcrumb()
        searchField.text = viewModel.searchText
        sortButton.apply(
            title: viewModel.sortType.title,
            image: CloudDriveAsset.image(named: "icon_cloud_storage_list_sort", fallbackSystemName: "arrow.up.arrow.down")
        )
        filterButton.apply(
            title: viewModel.filterType.displayTitle,
            image: CloudDriveAsset.image(named: "icon_cloud_storage_file_filter", fallbackSystemName: "line.3.horizontal.decrease.circle")
        )

        let isGrid = viewModel.displayMode == .grid
        let displayIcon = CloudDriveAsset.resizedImage(
            named: isGrid ? "icon_cloud_storage_show_type_list" : "icon_cloud_storage_show_type_table",
            fallbackSystemName: isGrid ? "list.bullet" : "square.grid.2x2",
            size: CGSize(width: 16, height: 16)
        )
        displayModeButton.setImage(displayIcon, for: .normal)

        let shouldReconfigureCells = appliedDisplayMode != nil && appliedDisplayMode != viewModel.displayMode
        if appliedDisplayMode != viewModel.displayMode {
            appliedDisplayMode = viewModel.displayMode
            collectionView.setCollectionViewLayout(makeLayout(), animated: false)
        }

        var snapshot = NSDiffableDataSourceSnapshot<Int, CloudFile>()
        snapshot.appendSections([0])
        snapshot.appendItems(viewModel.files)
        if shouldReconfigureCells {
            snapshot.reconfigureItems(viewModel.files)
        }
        dataSource.apply(snapshot, animatingDifferences: true)

        if !viewModel.isRefreshing {
            refreshControl.endRefreshing()
        }
        if viewModel.isLoading, !viewModel.files.isEmpty {
            loadingMoreIndicator.startAnimating()
        } else {
            loadingMoreIndicator.stopAnimating()
        }
        if viewModel.isLoading, viewModel.files.isEmpty, !refreshControl.isRefreshing {
            showLoading()
        } else {
            hideLoading()
        }
        updateInteractivePopGesture()
    }

    private func rebuildBreadcrumb() {
        pathStackView.arrangedSubviews.forEach {
            pathStackView.removeArrangedSubview($0)
            $0.removeFromSuperview()
        }

        let homeButton = UIButton(type: .system)
        homeButton.setImage(
            CloudDriveAsset.image(named: "icon_cloud_storage_go_home", fallbackSystemName: "house.fill")?.withRenderingMode(.alwaysOriginal),
            for: .normal
        )
        homeButton.imageView?.contentMode = .scaleAspectFit
        homeButton.accessibilityLabel = "返回云盘首页"
        homeButton.addTarget(self, action: #selector(homeTapped), for: .touchUpInside)
        homeButton.snp.makeConstraints { make in
            make.width.height.equalTo(16)
        }
        pathStackView.addArrangedSubview(homeButton)
        pathStackView.addArrangedSubview(makeBreadcrumbSpacer(width: 8))

        for (index, item) in viewModel.pathStack.enumerated() {
            if index > 0 {
                let container = UIView()
                let arrow = UIImageView(
                    image: CloudDriveAsset.image(named: "icon_cloud_storage_current_path_arrow", fallbackSystemName: "chevron.right")
                )
                arrow.contentMode = .scaleAspectFit
                container.addSubview(arrow)
                container.snp.makeConstraints { make in make.width.equalTo(32) }
                arrow.snp.makeConstraints { make in make.center.equalToSuperview(); make.width.height.equalTo(16) }
                pathStackView.addArrangedSubview(container)
            }

            let isLast = index == viewModel.pathStack.count - 1
            let button = UIButton(type: .system)
            button.setTitle(item.name, for: .normal)
            button.titleLabel?.font = .systemFont(ofSize: 16, weight: isLast ? .semibold : .regular)
            button.setTitleColor(isLast ? .black : UIColor(hex: 0x4B5563), for: .normal)
            button.titleLabel?.lineBreakMode = .byTruncatingTail
            button.tag = index
            button.isUserInteractionEnabled = !isLast
            if !isLast {
                button.addTarget(self, action: #selector(pathTapped(_:)), for: .touchUpInside)
            }
            pathStackView.addArrangedSubview(button)
        }

        pathScrollView.layoutIfNeeded()
        let rightOffset = max(0, pathScrollView.contentSize.width - pathScrollView.bounds.width)
        pathScrollView.setContentOffset(CGPoint(x: rightOffset, y: 0), animated: false)
    }

    private func makeBreadcrumbSpacer(width: CGFloat) -> UIView {
        let spacer = UIView()
        spacer.snp.makeConstraints { make in make.width.equalTo(width) }
        return spacer
    }

    private func makeLayout() -> UICollectionViewCompositionalLayout {
        UICollectionViewCompositionalLayout { [weak self] _, environment in
            switch self?.viewModel.displayMode ?? .grid {
            case .grid:
                let columns = 2
                let spacing: CGFloat = 15
                let horizontalInset: CGFloat = 12
                let availableWidth = environment.container.effectiveContentSize.width - horizontalInset * 2
                let itemWidth = (availableWidth - spacing) / CGFloat(columns)
                let itemHeight = itemWidth / 1.77 + 52
                let itemSize = NSCollectionLayoutSize(
                    widthDimension: .absolute(itemWidth),
                    heightDimension: .absolute(itemHeight)
                )
                let item = NSCollectionLayoutItem(layoutSize: itemSize)
                let groupSize = NSCollectionLayoutSize(
                    widthDimension: .fractionalWidth(1),
                    heightDimension: .absolute(itemHeight)
                )
                let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, repeatingSubitem: item, count: columns)
                group.interItemSpacing = .fixed(spacing)
                let section = NSCollectionLayoutSection(group: group)
                section.contentInsets = NSDirectionalEdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 12)
                section.interGroupSpacing = spacing
                return section
            case .list:
                let size = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(72))
                let item = NSCollectionLayoutItem(layoutSize: size)
                let group = NSCollectionLayoutGroup.vertical(layoutSize: size, subitems: [item])
                let section = NSCollectionLayoutSection(group: group)
                section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)
                return section
            }
        }
    }

    private func showControlSheet(for file: CloudFile) {
        viewModel.setControlFile(file)
        let sheet = CloudDriveActionSheetViewController(
            title: "请选择",
            layout: .controls,
            items: [
                .init(
                    title: "删除",
                    image: CloudDriveAsset.image(named: "icon_cloud_storage_file_delete", fallbackSystemName: "trash"),
                    isDestructive: false,
                    handler: { [weak self] in self?.confirmDelete() }
                ),
                .init(
                    title: "下载",
                    image: CloudDriveAsset.image(named: "icon_cloud_storage_file_download", fallbackSystemName: "arrow.down.circle"),
                    isDestructive: false,
                    handler: { [weak self] in self?.download(file) }
                ),
                .init(
                    title: "移动",
                    image: CloudDriveAsset.image(named: "icon_cloud_storage_file_move", fallbackSystemName: "folder"),
                    isDestructive: false,
                    handler: { [weak self] in self?.showMoveDialog() }
                ),
                .init(
                    title: "详情",
                    image: CloudDriveAsset.image(named: "icon_cloud_storage_file_details", fallbackSystemName: "info.circle"),
                    isDestructive: false,
                    handler: { [weak self] in self?.showDetailDialog(file) }
                ),
            ]
        )
        present(sheet, animated: false)
    }

    private func confirmDelete() {
        let dialog = CloudDriveConfirmationDialogViewController(
            title: "删除确认",
            message: "删除后无法恢复，确认删除吗？",
            onConfirm: { [weak self] in
                guard let self else { return }
                Task { await self.viewModel.deleteCurrentFile(api: self.api) }
            }
        )
        present(dialog, animated: false)
    }

    private func download(_ file: CloudFile) {
        guard file.isSelectableMedia else {
            showToast("当前类型不支持下载")
            return
        }
        guard !file.fileUrl.isEmpty else {
            showToast("文件地址不存在")
            return
        }
        transferManager.addDownloadTask(
            fileURL: file.fileUrl,
            fileName: file.name,
            fileType: file.type,
            fileSize: file.fileSize
        )
    }

    private func showMoveDialog() {
        let dialog = CloudDriveMoveDialogViewController(folders: viewModel.moveTargetFolders()) { [weak self] folderID in
            guard let self else { return }
            Task { await self.viewModel.moveCurrentFile(targetFolderId: folderID, api: self.api) }
        }
        present(dialog, animated: false)
    }

    private func showDetailDialog(_ file: CloudFile) {
        let sheet = CloudDriveDetailSheetViewController(file: file) { [weak self] name in
            guard let self else { return }
            Task { await self.viewModel.saveCurrentFileName(name, api: self.api) }
        }
        present(sheet, animated: false)
    }

    private func beginPick(fileType: Int) {
        Task {
            guard await viewModel.checkCanUpload(api: api) else { return }
            await MainActor.run {
                var configuration = PHPickerConfiguration(photoLibrary: .shared())
                configuration.selectionLimit = 1
                configuration.filter = fileType == 2 ? .images : .videos
                let picker = PHPickerViewController(configuration: configuration)
                picker.delegate = self
                picker.view.tag = fileType
                self.present(picker, animated: true)
            }
        }
    }

    private func showCreateFolderDialog() {
        guard viewModel.pathStack.count < 5 else {
            showToast("不能创建更深的文件夹")
            return
        }
        let dialog = CloudDriveTextInputDialogViewController(
            title: "新建文件夹",
            placeholder: "请输入文件夹名称"
        ) { [weak self] name in
            guard let self else { return }
            Task { await self.viewModel.addFolder(name: name, api: self.api) }
        }
        present(dialog, animated: false)
    }

    private func showDropdown(
        from sourceView: UIView,
        items: [CloudDriveDropdownViewController.Item]
    ) {
        present(CloudDriveDropdownViewController(sourceView: sourceView, items: items), animated: false)
    }

    private func updateInteractivePopGesture() {
        navigationController?.interactivePopGestureRecognizer?.isEnabled = viewModel.pathStack.count == 1
    }

    private func shouldHandleInteraction(_ key: String, interval: TimeInterval = 0.5) -> Bool {
        let now = ProcessInfo.processInfo.systemUptime
        if let previous = lastInteractionTimes[key], now - previous < interval {
            return false
        }
        lastInteractionTimes[key] = now
        return true
    }

    @objc private func backTapped() {
        guard shouldHandleInteraction("back", interval: 1) else { return }
        Task {
            let handled = await viewModel.navigateBack(api: api)
            if !handled {
                _ = await MainActor.run { self.navigationController?.popViewController(animated: true) }
            }
        }
    }

    @objc private func refreshTriggered() {
        Task { await viewModel.refresh(api: api, showLoading: false) }
    }

    @objc private func sortTapped() {
        guard shouldHandleInteraction("sort") else { return }
        showDropdown(
            from: sortButton,
            items: CloudDriveSortType.allCases.map { type in
                .init(title: type.title, isSelected: type == viewModel.sortType) { [weak self] in
                    guard let self else { return }
                    Task { await self.viewModel.chooseSortType(type, api: self.api) }
                }
            }
        )
    }

    @objc private func filterTapped() {
        guard shouldHandleInteraction("filter") else { return }
        showDropdown(
            from: filterButton,
            items: CloudDriveFilterType.androidMenuOrder.map { type in
                .init(title: type.menuTitle, isSelected: type == viewModel.filterType) { [weak self] in
                    guard let self else { return }
                    Task { await self.viewModel.chooseFilterType(type, api: self.api) }
                }
            }
        )
    }

    @objc private func uploadTapped() {
        guard shouldHandleInteraction("upload") else { return }
        let sheet = CloudDriveActionSheetViewController(
            title: "选择上传类型",
            layout: .upload,
            items: [
                .init(
                    title: "上传图片",
                    image: CloudDriveAsset.image(named: "icon_cloud_storage_add_photo", fallbackSystemName: "photo"),
                    isDestructive: false,
                    handler: { [weak self] in self?.beginPick(fileType: 2) }
                ),
                .init(
                    title: "上传视频",
                    image: CloudDriveAsset.image(named: "icon_cloud_storage_add_video", fallbackSystemName: "video"),
                    isDestructive: false,
                    handler: { [weak self] in self?.beginPick(fileType: 1) }
                ),
                .init(
                    title: "新建文件夹",
                    image: CloudDriveAsset.image(named: "icon_cloud_storage_add_folder", fallbackSystemName: "folder.badge.plus"),
                    isDestructive: false,
                    handler: { [weak self] in self?.showCreateFolderDialog() }
                ),
            ]
        )
        present(sheet, animated: false)
    }

    @objc private func displayModeTapped() {
        guard shouldHandleInteraction("display") else { return }
        viewModel.toggleDisplayMode()
    }

    @objc private func openTransferManager() {
        guard shouldHandleInteraction("transfer", interval: 1) else { return }
        navigationController?.pushViewController(CloudDriveTransferViewController(manager: transferManager), animated: true)
    }

    @objc private func homeTapped() {
        guard shouldHandleInteraction("home") else { return }
        Task { await viewModel.goHome(api: api) }
    }

    @objc private func pathTapped(_ sender: UIButton) {
        guard shouldHandleInteraction("path_\(sender.tag)") else { return }
        Task { await viewModel.navigateToPathIndex(sender.tag, api: api) }
    }

    private func openPreview(file: CloudFile) {
        navigationController?.pushViewController(CloudDrivePreviewViewController(file: file), animated: true)
    }

    private func stagePickedFile(result: PHPickerResult, fileType: Int) {
        let provider = result.itemProvider
        let typeIdentifier = fileType == 2 ? UTType.image.identifier : UTType.movie.identifier
        provider.loadFileRepresentation(forTypeIdentifier: typeIdentifier) { [weak self] url, error in
            guard let self else { return }
            if let error {
                Task { @MainActor in self.showToast(error.localizedDescription) }
                return
            }
            guard let url else { return }
            do {
                let stagedURL = try Self.copyPickedFileToStage(
                    url: url,
                    suggestedName: provider.suggestedName,
                    fileType: fileType
                )
                let attributes = try? FileManager.default.attributesOfItem(atPath: stagedURL.path)
                let size = (attributes?[.size] as? NSNumber)?.int64Value ?? 0
                Task { @MainActor in
                    self.transferManager.addUploadTask(
                        localFileURL: stagedURL,
                        fileName: stagedURL.lastPathComponent,
                        fileType: fileType,
                        fileSize: size,
                        parentFolderId: self.viewModel.currentFolderID
                    )
                }
            } catch {
                Task { @MainActor in self.showToast(error.localizedDescription) }
            }
        }
    }

    private static func copyPickedFileToStage(url: URL, suggestedName: String?, fileType: Int) throws -> URL {
        let directory = FileManager.default.temporaryDirectory.appendingPathComponent("cloud_drive_uploads", isDirectory: true)
        try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
        let ext = url.pathExtension.isEmpty ? (fileType == 2 ? "jpg" : "mp4") : url.pathExtension
        let baseName = CloudTransferFileName.sanitized(suggestedName ?? "cloud_upload")
        let fileName = baseName.lowercased().hasSuffix(".\(ext.lowercased())") ? baseName : "\(baseName).\(ext)"
        let destination = directory.appendingPathComponent("\(UUID().uuidString.replacingOccurrences(of: "-", with: ""))_\(fileName)")
        try FileManager.default.copyItem(at: url, to: destination)
        return destination
    }
}

extension CloudDriveListViewController: UITextFieldDelegate {
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        viewModel.updateSearchText(textField.text ?? "")
        textField.resignFirstResponder()
        Task { await viewModel.refresh(api: api, showLoading: true) }
        return true
    }
}

extension CloudDriveListViewController: UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        guard let file = dataSource.itemIdentifier(for: indexPath),
              shouldHandleInteraction("file_\(file.id)") else { return }
        Task {
            if let preview = await viewModel.handleFileTap(file, api: api) {
                await MainActor.run { self.openPreview(file: preview) }
            }
        }
    }

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard scrollView.contentSize.height > scrollView.bounds.height else { return }
        let threshold = scrollView.contentSize.height - scrollView.bounds.height - 120
        guard scrollView.contentOffset.y >= threshold else { return }
        let lastIndex = max(0, viewModel.files.count - 1)
        Task { await viewModel.loadMoreIfNeeded(lastVisibleIndex: lastIndex, api: api) }
    }
}

extension CloudDriveListViewController: PHPickerViewControllerDelegate {
    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        let fileType = picker.view.tag
        picker.dismiss(animated: true)
        guard let result = results.first else { return }
        stagePickedFile(result: result, fileType: fileType)
    }
}

/// Android 云盘筛选条按钮，保证文案与图标分居两端。
private final class CloudDriveMenuButton: UIButton {
    private let valueLabel = UILabel()
    private let iconView = UIImageView()

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = UIColor(hex: 0xF4F4F4)
        layer.cornerRadius = 4
        valueLabel.font = .systemFont(ofSize: 14)
        valueLabel.textColor = UIColor(hex: 0x333333)
        valueLabel.isUserInteractionEnabled = false
        iconView.contentMode = .scaleAspectFit
        iconView.isUserInteractionEnabled = false
        addSubview(valueLabel)
        addSubview(iconView)
        valueLabel.snp.makeConstraints { make in
            make.leading.equalToSuperview().offset(12)
            make.centerY.equalToSuperview()
            make.trailing.lessThanOrEqualTo(iconView.snp.leading).offset(-8)
        }
        iconView.snp.makeConstraints { make in
            make.trailing.equalToSuperview().inset(12)
            make.centerY.equalToSuperview()
            make.width.height.equalTo(16)
        }
    }

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

    /// 更新按钮文案与图标。
    func apply(title: String, image: UIImage?) {
        valueLabel.text = title
        iconView.image = image?.withRenderingMode(.alwaysOriginal)
        accessibilityLabel = title
    }
}

/// Android 风格的轻量下拉菜单。
private final class CloudDriveDropdownViewController: UIViewController {
    /// 下拉菜单项。
    struct Item {
        let title: String
        let isSelected: Bool
        let handler: () -> Void
    }

    private weak var sourceView: UIView?
    private let items: [Item]
    private let dismissButton = UIButton(type: .custom)
    private let menuView = UIView()
    private let stackView = UIStackView()

    /// 创建锚定到指定按钮的下拉菜单。
    init(sourceView: UIView, items: [Item]) {
        self.sourceView = sourceView
        self.items = items
        super.init(nibName: nil, bundle: nil)
        modalPresentationStyle = .overFullScreen
    }

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

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .clear
        dismissButton.addTarget(self, action: #selector(dismissTapped), for: .touchUpInside)
        menuView.backgroundColor = .white
        menuView.layer.cornerRadius = 8
        menuView.layer.shadowColor = UIColor.black.cgColor
        menuView.layer.shadowOpacity = 0.16
        menuView.layer.shadowRadius = 10
        menuView.layer.shadowOffset = CGSize(width: 0, height: 4)
        stackView.axis = .vertical

        view.addSubview(dismissButton)
        view.addSubview(menuView)
        menuView.addSubview(stackView)
        dismissButton.snp.makeConstraints { make in make.edges.equalToSuperview() }
        stackView.snp.makeConstraints { make in make.edges.equalToSuperview() }

        items.enumerated().forEach { index, item in
            let button = UIButton(type: .system)
            var configuration = UIButton.Configuration.plain()
            configuration.title = item.title
            configuration.baseForegroundColor = item.isSelected ? AppColor.primary : UIColor(hex: 0x333333)
            configuration.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)
            configuration.titleAlignment = .leading
            configuration.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { attributes in
                var attributes = attributes
                attributes.font = .systemFont(ofSize: 14)
                return attributes
            }
            button.configuration = configuration
            button.tag = index
            button.addTarget(self, action: #selector(itemTapped(_:)), for: .touchUpInside)
            button.snp.makeConstraints { make in make.height.equalTo(48) }
            stackView.addArrangedSubview(button)
        }
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        guard let sourceView else { return }
        let sourceFrame = sourceView.convert(sourceView.bounds, to: view)
        let width: CGFloat = 165
        let height = CGFloat(items.count) * 48
        let x = min(max(8, sourceFrame.minX), view.bounds.width - width - 8)
        var y = sourceFrame.maxY
        if y + height > view.bounds.height - 8 {
            y = sourceFrame.minY - height
        }
        menuView.frame = CGRect(x: x, y: y, width: width, height: height)
    }

    @objc private func dismissTapped() {
        dismiss(animated: false)
    }

    @objc private func itemTapped(_ sender: UIButton) {
        guard items.indices.contains(sender.tag) else { return }
        let handler = items[sender.tag].handler
        dismiss(animated: false, completion: handler)
    }
}
