//
//  TaskAddViewController.swift
//  suixinkan
//

import AVFoundation
import PhotosUI
import SnapKit
import UIKit
import UniformTypeIdentifiers

/// 添加任务页，对齐 Android `TaskAddScreen`。
final class TaskAddViewController: BaseViewController {

    private let viewModel = TaskAddViewModel()
    private let taskAPI = NetworkServices.shared.taskAPI
    private let ossUploadService = NetworkServices.shared.ossUploadService

    private let scrollView = UIScrollView()
    private let formCardView = UIView()
    private let contentStack = UIStackView()
    private let mediaSection = TaskAddMediaSectionView()
    private let prioritySection = TaskPrioritySectionView()
    private let orderTitleLabel = UILabel()
    private let orderButton = TaskOrderSelectButton()
    private let nameField = TaskBorderTextField(placeholder: "请输入任务标题")
    private let detailsField = TaskBorderTextView(placeholder: "请输入任务描述（必填）")
    private let saveButton = AppButton(title: "保存")
    private let bottomBar = UIView()

    private var remarkDialog: TaskFileRemarkDialogView?
    private var uploadTypeSheet: TaskUploadTypeSheetView?
    private var pickingMediaType: Int = 2
    private var detailsPlaceholder = "请输入任务描述（必填）"

    override func setupNavigationBar() {
        title = "添加任务"
    }

    override func setupUI() {
        view.backgroundColor = AppColor.pageBackground
        scrollView.backgroundColor = AppColor.pageBackground
        scrollView.keyboardDismissMode = .interactive

        formCardView.backgroundColor = .white
        formCardView.layer.cornerRadius = AppRadius.lg

        contentStack.axis = .vertical
        contentStack.spacing = AppSpacing.md

        orderTitleLabel.text = "关联订单（可选）"
        orderTitleLabel.font = .app(.bodyMedium)
        orderTitleLabel.textColor = AppColor.textPrimary

        bottomBar.backgroundColor = .white
        view.addSubview(scrollView)
        view.addSubview(bottomBar)
        scrollView.addSubview(formCardView)
        formCardView.addSubview(contentStack)

        contentStack.addArrangedSubview(mediaSection)
        contentStack.addArrangedSubview(prioritySection)
        contentStack.addArrangedSubview(orderTitleLabel)
        contentStack.addArrangedSubview(orderButton)
        contentStack.addArrangedSubview(nameField)
        contentStack.addArrangedSubview(detailsField)
        contentStack.setCustomSpacing(AppSpacing.xxs, after: orderTitleLabel)
        bottomBar.addSubview(saveButton)
    }

    override func setupConstraints() {
        bottomBar.snp.makeConstraints { make in
            make.leading.trailing.bottom.equalToSuperview()
            make.height.equalTo(80)
        }
        saveButton.snp.makeConstraints { make in
            make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
            make.centerY.equalToSuperview()
        }
        scrollView.snp.makeConstraints { make in
            make.top.leading.trailing.equalTo(view.safeAreaLayoutGuide)
            make.bottom.equalTo(bottomBar.snp.top)
        }
        formCardView.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(AppSpacing.md)
            make.width.equalTo(scrollView.snp.width).offset(-AppSpacing.md * 2)
        }
        contentStack.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(AppSpacing.md)
        }
    }

    override func bindActions() {
        viewModel.onStateChange = { [weak self] in
            Task { @MainActor in self?.applyViewModel() }
        }
        viewModel.onShowMessage = { [weak self] message in
            Task { @MainActor in self?.showToast(message) }
        }

        mediaSection.onCloudImport = { [weak self] in self?.openCloudPicker() }
        mediaSection.onLocalImport = { [weak self] in self?.presentLocalImportSheet() }
        mediaSection.onFileTap = { [weak self] file in self?.viewModel.presentRemarkDialog(for: file.id) }
        mediaSection.onDeleteFile = { [weak self] file in self?.confirmDelete(file: file) }

        prioritySection.onUrgentHourChange = { [weak self] hour in
            self?.viewModel.setUrgentHour(hour)
        }
        prioritySection.onCustomHourTextChange = { [weak self] text in
            self?.viewModel.updateCustomUrgentHourText(text)
        }
        prioritySection.onShowMessage = { [weak self] message in
            self?.showToast(message)
        }

        orderButton.addTarget(self, action: #selector(openOrderSelect), for: .touchUpInside)
        nameField.textField.addTarget(self, action: #selector(nameChanged), for: .editingChanged)
        detailsField.textView.delegate = self
        saveButton.addTarget(self, action: #selector(saveTapped), for: .touchUpInside)
        applyViewModel()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        if let fileID = viewModel.consumePendingRemarkFileID() {
            viewModel.presentRemarkDialog(for: fileID)
        }
    }

    @MainActor
    private func applyViewModel() {
        mediaSection.apply(files: viewModel.selectedFiles)
        prioritySection.apply(urgentHour: viewModel.urgentHour)
        orderButton.apply(orderNumber: viewModel.selectedOrderNumber)
        if nameField.textField.text != viewModel.taskName {
            nameField.textField.text = viewModel.taskName
        }
        if viewModel.taskDetails.isEmpty, !detailsField.textView.isFirstResponder {
            detailsField.textView.text = detailsPlaceholder
            detailsField.textView.textColor = AppColor.textTertiary
        } else if detailsField.textView.text != viewModel.taskDetails {
            detailsField.textView.text = viewModel.taskDetails
            detailsField.textView.textColor = AppColor.textPrimary
        }
        saveButton.isEnabled = true
        saveButton.alpha = viewModel.isUploading ? 0.6 : 1

        if viewModel.showRemarkDialog, let file = viewModel.remarkTargetFile {
            showRemarkDialog(file: file)
        } else {
            hideRemarkDialog()
        }

        if viewModel.showSuccessDialog {
            presentSuccessDialog()
        }
    }

    @objc private func nameChanged() {
        viewModel.updateTaskName(nameField.textField.text ?? "")
    }

    @objc private func openOrderSelect() {
        let controller = TaskOrderSelectViewController(initialOrderNumber: viewModel.selectedOrderNumber)
        controller.onConfirmed = { [weak self] orderNumber in
            self?.viewModel.setSelectedOrderNumber(orderNumber)
        }
        navigationController?.pushViewController(controller, animated: true)
    }

    private func openCloudPicker() {
        let controller = CloudStoragePickForTaskViewController(
            importedFileIDs: viewModel.importedCloudFileIDs
        )
        controller.onConfirmed = { [weak self] files in
            self?.viewModel.addCloudFiles(files)
            if let fileID = self?.viewModel.consumePendingRemarkFileID() {
                self?.viewModel.presentRemarkDialog(for: fileID)
            }
        }
        navigationController?.pushViewController(controller, animated: true)
    }

    private func presentLocalImportSheet() {
        guard uploadTypeSheet == nil else { return }
        let sheet = TaskUploadTypeSheetView(frame: view.bounds)
        sheet.onChooseImage = { [weak self] in
            self?.hideUploadTypeSheet {
                self?.presentLocalSourcePicker(isImage: true)
            }
        }
        sheet.onChooseVideo = { [weak self] in
            self?.hideUploadTypeSheet {
                self?.presentLocalSourcePicker(isImage: false)
            }
        }
        sheet.onCancel = { [weak self] in
            self?.hideUploadTypeSheet()
        }
        uploadTypeSheet = sheet
        view.addSubview(sheet)
        sheet.snp.makeConstraints { make in make.edges.equalToSuperview() }
        sheet.show()
    }

    private func hideUploadTypeSheet(completion: (() -> Void)? = nil) {
        uploadTypeSheet?.dismiss { [weak self] in
            self?.uploadTypeSheet?.removeFromSuperview()
            self?.uploadTypeSheet = nil
            completion?()
        }
    }

    private func presentLocalSourcePicker(isImage: Bool) {
        pickingMediaType = isImage ? 2 : 1
        let sheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
        if UIImagePickerController.isSourceTypeAvailable(.camera) {
            sheet.addAction(UIAlertAction(title: isImage ? "拍照" : "拍摄视频", style: .default) { [weak self] _ in
                self?.presentCamera(isImage: isImage)
            })
        }
        sheet.addAction(UIAlertAction(title: "从相册选择", style: .default) { [weak self] _ in
            self?.presentMediaPicker(isImage: isImage)
        })
        sheet.addAction(UIAlertAction(title: "取消", style: .cancel))
        present(sheet, animated: true)
    }

    private func presentMediaPicker(isImage: Bool) {
        pickingMediaType = isImage ? 2 : 1
        var configuration = PHPickerConfiguration(photoLibrary: .shared())
        configuration.filter = isImage ? .images : .videos
        configuration.selectionLimit = 50
        let picker = PHPickerViewController(configuration: configuration)
        picker.delegate = self
        present(picker, animated: true)
    }

    private func presentCamera(isImage: Bool) {
        let picker = UIImagePickerController()
        picker.sourceType = .camera
        picker.delegate = self
        picker.mediaTypes = [isImage ? UTType.image.identifier : UTType.movie.identifier]
        picker.cameraCaptureMode = isImage ? .photo : .video
        if !isImage {
            picker.videoQuality = .typeHigh
        }
        present(picker, animated: true)
    }

    private func confirmDelete(file: TaskFileItem) {
        let alert = UIAlertController(title: "删除确认", message: "确定要删除此文件吗？", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "取消", style: .cancel))
        alert.addAction(UIAlertAction(title: "确定", style: .default) { [weak self] _ in
            self?.viewModel.removeTaskFile(id: file.id)
        })
        present(alert, animated: true)
    }

    @objc private func saveTapped() {
        showLoading()
        Task {
            await viewModel.validateAndSubmit(api: taskAPI)
            hideLoading()
        }
    }

    private func presentSuccessDialog() {
        guard viewModel.showSuccessDialog else { return }
        let alert = UIAlertController(
            title: "添加成功",
            message: "任务已成功添加。是否继续添加新任务？",
            preferredStyle: .alert
        )
        alert.addAction(UIAlertAction(title: "继续添加", style: .default) { [weak self] _ in
            self?.viewModel.handleContinueAdding()
        })
        alert.addAction(UIAlertAction(title: "返回", style: .cancel) { [weak self] _ in
            self?.viewModel.dismissSuccessDialog()
            self?.navigationController?.popViewController(animated: true)
        })
        present(alert, animated: true)
        viewModel.dismissSuccessDialog()
    }

    private func showRemarkDialog(file: TaskFileItem) {
        if remarkDialog == nil {
            let dialog = TaskFileRemarkDialogView(frame: view.bounds)
            dialog.onCancel = { [weak self] in
                self?.viewModel.hideRemarkDialog()
            }
            dialog.onConfirm = { [weak self] in
                guard let self, let dialog = self.remarkDialog else { return }
                self.viewModel.updateRemarkDialogText(dialog.inputText)
                self.viewModel.saveRemarkDialog()
            }
            remarkDialog = dialog
            view.addSubview(dialog)
            dialog.snp.makeConstraints { make in make.edges.equalToSuperview() }
        }
        remarkDialog?.apply(file: file, text: viewModel.remarkDialogText)
    }

    private func hideRemarkDialog() {
        remarkDialog?.removeFromSuperview()
        remarkDialog = nil
    }

    private func uploadPickedResults(_ results: [PHPickerResult], isImage: Bool) {
        let scenicId = AppStore.shared.session.currentScenicId
        guard scenicId > 0 else {
            showToast("请先选择景区")
            return
        }
        let totalCount = results.count
        for result in results {
            let uploadTaskId = UUID().uuidString.replacingOccurrences(of: "-", with: "")
            viewModel.addUploadPlaceholder(
                fileName: TaskMediaLoader.suggestedFileName(
                    from: result.itemProvider.suggestedName,
                    fallback: "upload_\(uploadTaskId).\(isImage ? "jpg" : "mp4")"
                ),
                fileType: isImage ? 2 : 1,
                uploadTaskId: uploadTaskId
            )
            Task {
                do {
                    let payload = try await TaskMediaLoader.load(from: result, isImage: isImage)
                    await uploadPayload(
                        payload,
                        fileType: isImage ? 2 : 1,
                        uploadTaskId: uploadTaskId,
                        totalCount: totalCount,
                        scenicId: scenicId
                    )
                } catch {
                    await MainActor.run {
                        self.viewModel.markUploadFailed(uploadTaskId: uploadTaskId)
                        self.showToast(error.localizedDescription)
                    }
                }
            }
        }
    }

    private func uploadCapturedPayload(_ payload: TaskMediaLoader.Payload, fileType: Int) {
        let scenicId = AppStore.shared.session.currentScenicId
        guard scenicId > 0 else {
            showToast("请先选择景区")
            return
        }
        let uploadTaskId = UUID().uuidString.replacingOccurrences(of: "-", with: "")
        viewModel.addUploadPlaceholder(
            fileName: payload.fileName,
            fileType: fileType,
            uploadTaskId: uploadTaskId
        )
        Task {
            await uploadPayload(
                payload,
                fileType: fileType,
                uploadTaskId: uploadTaskId,
                totalCount: 1,
                scenicId: scenicId
            )
        }
    }

    private func uploadPayload(
        _ payload: TaskMediaLoader.Payload,
        fileType: Int,
        uploadTaskId: String,
        totalCount: Int,
        scenicId: Int
    ) async {
        viewModel.prepareUpload(
            uploadTaskId: uploadTaskId,
            fileName: payload.fileName,
            localPreviewData: payload.previewData
        )
        do {
            let fileURL = try await ossUploadService.uploadTaskFile(
                data: payload.data,
                fileName: payload.fileName,
                fileType: fileType,
                scenicId: scenicId
            ) { [weak self] progress in
                Task { @MainActor in
                    self?.viewModel.updateUploadProgress(uploadTaskId: uploadTaskId, progress: progress)
                }
            }
            viewModel.markUploadSucceeded(
                uploadTaskId: uploadTaskId,
                fileURL: fileURL,
                fileName: payload.fileName,
                localPreviewData: payload.previewData,
                autoRemarkWhenSingle: totalCount == 1
            )
            if totalCount == 1, let fileID = viewModel.consumePendingRemarkFileID() {
                viewModel.presentRemarkDialog(for: fileID)
            }
        } catch {
            viewModel.markUploadFailed(uploadTaskId: uploadTaskId)
            showToast(error.localizedDescription)
        }
    }
}

extension TaskAddViewController: PHPickerViewControllerDelegate {
    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        picker.dismiss(animated: true)
        guard !results.isEmpty else { return }
        uploadPickedResults(results, isImage: pickingMediaType == 2)
    }
}

extension TaskAddViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        picker.dismiss(animated: true)
    }

    func imagePickerController(
        _ picker: UIImagePickerController,
        didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]
    ) {
        picker.dismiss(animated: true)
        if pickingMediaType == 1,
           let url = info[.mediaURL] as? URL,
           let payload = try? TaskMediaLoader.videoPayload(from: url) {
            uploadCapturedPayload(payload, fileType: 1)
            return
        }
        if let image = info[.originalImage] as? UIImage,
           let payload = TaskMediaLoader.imagePayload(
               from: image,
               fileName: "image_\(Int(Date().timeIntervalSince1970)).jpg"
           ) {
            uploadCapturedPayload(payload, fileType: 2)
        }
    }
}

extension TaskAddViewController: UITextViewDelegate {
    func textViewDidBeginEditing(_ textView: UITextView) {
        if textView.textColor == AppColor.textTertiary {
            textView.text = ""
            textView.textColor = AppColor.textPrimary
        }
    }

    func textViewDidEndEditing(_ textView: UITextView) {
        if textView.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
            textView.text = detailsPlaceholder
            textView.textColor = AppColor.textTertiary
            viewModel.updateTaskDetails("")
        }
    }

    func textViewDidChange(_ textView: UITextView) {
        guard textView.textColor != AppColor.textTertiary else { return }
        viewModel.updateTaskDetails(textView.text)
    }
}

/// 从 PHPicker 结果加载上传数据。
enum TaskMediaLoader {
    /// 已读取、可直接上传的任务素材。
    struct Payload {
        let data: Data
        let fileName: String
        let previewData: Data?
    }

    /// 优先使用系统相册提供的原文件名。
    static func suggestedFileName(from suggestedFileName: String?, fallback: String) -> String {
        guard let suggestedName = suggestedFileName?.trimmingCharacters(in: .whitespacesAndNewlines),
              !suggestedName.isEmpty else {
            return fallback
        }
        if !URL(fileURLWithPath: suggestedName).pathExtension.isEmpty {
            return suggestedName
        }
        let fallbackExtension = URL(fileURLWithPath: fallback).pathExtension
        return fallbackExtension.isEmpty ? suggestedName : "\(suggestedName).\(fallbackExtension)"
    }

    static func load(from result: PHPickerResult, isImage: Bool) async throws -> Payload {
        let provider = result.itemProvider
        if isImage {
            return try await loadImage(from: provider)
        }
        return try await loadVideo(from: provider)
    }

    private static func loadImage(from provider: NSItemProvider) async throws -> Payload {
        let suggestedName = provider.suggestedName
        return try await withCheckedThrowingContinuation { continuation in
            let identifier = provider.registeredTypeIdentifiers.first {
                UTType($0)?.conforms(to: .image) == true
            } ?? UTType.image.identifier
            provider.loadFileRepresentation(forTypeIdentifier: identifier) { url, error in
                if let error {
                    continuation.resume(throwing: error)
                    return
                }
                guard let url, let data = try? Data(contentsOf: url), !data.isEmpty else {
                    continuation.resume(throwing: OSSUploadError.emptyFile)
                    return
                }
                let ext = url.pathExtension.isEmpty ? "jpg" : url.pathExtension
                let fallback = "image_\(UUID().uuidString).\(ext)"
                let fileName = suggestedFileName(from: suggestedName, fallback: fallback)
                let previewData = UIImage(data: data).flatMap(makePreviewData(from:))
                continuation.resume(returning: Payload(data: data, fileName: fileName, previewData: previewData))
            }
        }
    }

    private static func loadVideo(from provider: NSItemProvider) async throws -> Payload {
        let typeIdentifier = UTType.movie.identifier
        let suggestedName = provider.suggestedName
        return try await withCheckedThrowingContinuation { continuation in
            provider.loadFileRepresentation(forTypeIdentifier: typeIdentifier) { url, error in
                if let error {
                    continuation.resume(throwing: error)
                    return
                }
                guard let url, let data = try? Data(contentsOf: url) else {
                    continuation.resume(throwing: OSSUploadError.emptyFile)
                    return
                }
                let ext = url.pathExtension.isEmpty ? "mp4" : url.pathExtension
                let fallback = "video_\(UUID().uuidString).\(ext)"
                let fileName = suggestedFileName(from: suggestedName, fallback: fallback)
                continuation.resume(returning: Payload(
                    data: data,
                    fileName: fileName,
                    previewData: makeVideoPreviewData(from: url)
                ))
            }
        }
    }

    /// 把相机照片转换为上传素材。
    static func imagePayload(from image: UIImage, fileName: String) -> Payload? {
        guard let data = image.jpegData(compressionQuality: 0.9) else { return nil }
        return Payload(data: data, fileName: fileName, previewData: makePreviewData(from: image))
    }

    /// 读取相机拍摄的视频及缩略图。
    static func videoPayload(from url: URL) throws -> Payload {
        let data = try Data(contentsOf: url)
        guard !data.isEmpty else { throw OSSUploadError.emptyFile }
        let ext = url.pathExtension.isEmpty ? "mov" : url.pathExtension
        let fileName = url.lastPathComponent.isEmpty
            ? "video_\(UUID().uuidString).\(ext)"
            : url.lastPathComponent
        return Payload(data: data, fileName: fileName, previewData: makeVideoPreviewData(from: url))
    }

    private static func makePreviewData(from image: UIImage) -> Data? {
        let maxLength: CGFloat = 600
        let scale = min(1, maxLength / max(image.size.width, image.size.height))
        let size = CGSize(width: image.size.width * scale, height: image.size.height * scale)
        let renderer = UIGraphicsImageRenderer(size: size)
        let preview = renderer.image { _ in
            image.draw(in: CGRect(origin: .zero, size: size))
        }
        return preview.jpegData(compressionQuality: 0.72)
    }

    private static func makeVideoPreviewData(from url: URL) -> Data? {
        let generator = AVAssetImageGenerator(asset: AVAsset(url: url))
        generator.appliesPreferredTrackTransform = true
        guard let image = try? generator.copyCGImage(at: .zero, actualTime: nil) else { return nil }
        return makePreviewData(from: UIImage(cgImage: image))
    }
}
