//
//  WildPhotographerReportSubmitViewController.swift
//  suixinkan
//

import PhotosUI
import SnapKit
import UIKit

/// 提交举报页面，提供类型、证据、说明、定位和联系方式表单。
final class WildPhotographerReportSubmitViewController: BaseViewController {
    private let viewModel: WildPhotographerReportSubmitViewModel
    private let homeViewModel: WildPhotographerReportHomeViewModel
    private let api: any WildPhotographerReportServing
    private let uploader: any WildReportAttachmentUploading
    private let scrollView = UIScrollView()
    private let contentStack = UIStackView()
    private let descriptionView = UITextView()
    private let descriptionPlaceholderLabel = UILabel()
    private let descriptionCountLabel = UILabel()
    private let contactField = UITextField()
    private let submitButton = UIButton(type: .system)
    private var pickerTarget: WildReportPickerTarget = .image
    private weak var activeInputView: UIView?
    private var isShowingSubmitLoading = false

    init(
        homeViewModel: WildPhotographerReportHomeViewModel,
        locationProvider: any LocationProviding = LocationProvider.shared,
        api: (any WildPhotographerReportServing)? = nil,
        uploader: (any WildReportAttachmentUploading)? = nil
    ) {
        self.homeViewModel = homeViewModel
        self.api = api ?? NetworkServices.shared.wildPhotographerReportAPI
        self.uploader = uploader ?? NetworkServices.shared.ossUploadService
        self.viewModel = WildPhotographerReportSubmitViewModel(
            homeViewModel: homeViewModel,
            locationProvider: locationProvider
        )
        super.init(nibName: nil, bundle: nil)
    }

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

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    override func setupNavigationBar() {
        title = "提交举报"
    }

    override func setupUI() {
        view.backgroundColor = AppColor.pageBackgroundSoft
        scrollView.keyboardDismissMode = .onDrag
        scrollView.showsVerticalScrollIndicator = false
        contentStack.axis = .vertical
        contentStack.spacing = AppSpacing.sm

        descriptionView.font = .systemFont(ofSize: 15)
        descriptionView.textColor = AppColor.textPrimary
        descriptionView.backgroundColor = .clear
        descriptionView.textContainerInset = UIEdgeInsets(top: 10, left: 8, bottom: 34, right: 8)
        descriptionView.delegate = self

        descriptionPlaceholderLabel.text = "请描述摄影师的位置、特征、行为..."
        descriptionPlaceholderLabel.font = .systemFont(ofSize: 15)
        descriptionPlaceholderLabel.textColor = UIColor(hex: 0x9CA3AF)

        descriptionCountLabel.font = .systemFont(ofSize: 14)
        descriptionCountLabel.textColor = UIColor(hex: 0x6B7280)
        descriptionCountLabel.textAlignment = .right

        contactField.placeholder = "请输入摄影师微信号或手机号（选填）"
        contactField.font = .app(.body)
        contactField.textColor = AppColor.textPrimary
        contactField.backgroundColor = UIColor(hex: 0xFBFCFE)
        contactField.layer.cornerRadius = 10
        contactField.layer.borderWidth = 1
        contactField.layer.borderColor = UIColor(hex: 0xCDD5E1).cgColor
        contactField.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 12, height: 1))
        contactField.leftViewMode = .always
        contactField.delegate = self
        contactField.addTarget(self, action: #selector(contactChanged), for: .editingChanged)

        submitButton.setTitle("提交举报", for: .normal)
        submitButton.titleLabel?.font = .systemFont(ofSize: 22, weight: .bold)
        submitButton.setTitleColor(.white, for: .normal)
        submitButton.backgroundColor = AppColor.primary
        submitButton.layer.cornerRadius = 14
        submitButton.layer.shadowColor = AppColor.primary.cgColor
        submitButton.layer.shadowOpacity = 0.22
        submitButton.layer.shadowRadius = 12
        submitButton.layer.shadowOffset = CGSize(width: 0, height: 6)
        submitButton.addTarget(self, action: #selector(submitTapped), for: .touchUpInside)

        view.addSubview(scrollView)
        scrollView.addSubview(contentStack)
        let dismissTap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
        dismissTap.cancelsTouchesInView = false
        dismissTap.delegate = self
        scrollView.addGestureRecognizer(dismissTap)
        registerKeyboardNotifications()
        rebuildContent()
    }

    override func setupConstraints() {
        scrollView.snp.makeConstraints { make in
            make.top.leading.trailing.equalTo(view.safeAreaLayoutGuide)
            make.bottom.equalToSuperview()
        }
        contentStack.snp.makeConstraints { make in
            make.edges.equalTo(scrollView.contentLayoutGuide).inset(UIEdgeInsets(top: 14, left: 18, bottom: 26, right: 18))
            make.width.equalTo(scrollView.frameLayoutGuide).offset(-36)
        }
    }

    override func bindActions() {
        viewModel.onStateChange = { [weak self] in
            Task { @MainActor in
                guard let self else { return }
                self.updateSubmitLoading()
                self.rebuildContent()
            }
        }
        viewModel.onShowMessage = { [weak self] message in
            Task { @MainActor in self?.showToast(message) }
        }
        viewModel.onSubmitSuccess = { [weak self] result in
            Task { @MainActor in
                guard let self else { return }
                self.navigationController?.pushViewController(
                    WildPhotographerReportSuccessViewController(
                        result: result,
                        homeViewModel: self.homeViewModel
                    ),
                    animated: true
                )
            }
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        viewModel.loadInitialLocationIfNeeded()
        Task {
            await viewModel.loadReportTypes(api: api)
        }
    }

    @MainActor
    private func rebuildContent() {
        contentStack.arrangedSubviews.forEach { view in
            contentStack.removeArrangedSubview(view)
            view.removeFromSuperview()
        }
        contentStack.addArrangedSubview(makeRealNameBanner())
        contentStack.addArrangedSubview(makeTypeCard())
        contentStack.addArrangedSubview(makeEvidenceCard())
        contentStack.addArrangedSubview(makeDescriptionCard())
        contentStack.addArrangedSubview(makeLocationCard())
        contentStack.addArrangedSubview(makeContactCard())
        let paymentCard = makePaymentCard()
        contentStack.addArrangedSubview(paymentCard)
        contentStack.setCustomSpacing(16, after: paymentCard)
        contentStack.addArrangedSubview(submitButton)
        submitButton.setTitle("提交举报", for: .normal)
        submitButton.isEnabled = !viewModel.isSubmitting
        submitButton.alpha = viewModel.isSubmitting ? 0.78 : 1
        submitButton.snp.remakeConstraints { make in
            make.height.equalTo(56)
        }
        if activeInputView?.isFirstResponder == true {
            scrollActiveInputIntoView(animated: false)
        }
    }

    private func makeRealNameBanner() -> UIView {
        let banner = UIView()
        banner.backgroundColor = UIColor(hex: 0xF6FAFF)
        banner.layer.cornerRadius = 16
        banner.layer.borderWidth = 1
        banner.layer.borderColor = UIColor(hex: 0xBFD7FF).cgColor

        let iconWrap = UIView()
        iconWrap.backgroundColor = AppColor.primary.withAlphaComponent(0.10)
        iconWrap.layer.cornerRadius = 17
        let icon = UIImageView(image: UIImage(systemName: "checkmark.shield.fill"))
        icon.tintColor = AppColor.primary
        iconWrap.addSubview(icon)
        icon.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.size.equalTo(22)
        }

        let message = UILabel()
        message.numberOfLines = 0
        message.attributedText = realNameBannerText()

        banner.addSubview(iconWrap)
        banner.addSubview(message)
        iconWrap.snp.makeConstraints { make in
            make.top.leading.equalToSuperview().offset(16)
            make.size.equalTo(34)
            make.bottom.lessThanOrEqualToSuperview().inset(16)
        }
        message.snp.makeConstraints { make in
            make.top.trailing.bottom.equalToSuperview().inset(16)
            make.leading.equalTo(iconWrap.snp.trailing).offset(12)
        }
        return banner
    }

    private func makeTypeCard() -> UIView {
        let card = sectionCard(index: 1, title: "举报类型")

        if viewModel.isLoadingReportTypes {
            let loading = WildReportUI.label("正在同步举报类型...", font: .systemFont(ofSize: 13), color: AppColor.textSecondary)
            card.stack.addArrangedSubview(loading)
        }

        let grid = UIStackView()
        grid.axis = .vertical
        grid.spacing = 8

        let types = viewModel.reportTypes
        if types.isEmpty, !viewModel.isLoadingReportTypes {
            let empty = WildReportUI.label("暂无可选举报类型，请稍后重试", font: .systemFont(ofSize: 13), color: AppColor.textSecondary)
            card.stack.addArrangedSubview(empty)
            return card
        }
        stride(from: 0, to: types.count, by: 3).forEach { startIndex in
            let rowTypes = Array(types[startIndex ..< min(startIndex + 3, types.count)])
            let row = UIStackView()
            row.axis = .horizontal
            row.spacing = 8
            row.distribution = .fillEqually

            rowTypes.forEach { type in
                let button = WildReportTypeOptionButton()
                button.apply(type: type, isSelected: viewModel.selectedReportType == type)
                button.addAction(UIAction { [weak self] _ in self?.viewModel.selectReportType(type) }, for: .touchUpInside)
                button.snp.makeConstraints { make in make.height.equalTo(72) }
                row.addArrangedSubview(button)
            }
            for _ in rowTypes.count..<3 {
                row.addArrangedSubview(UIView())
            }
            grid.addArrangedSubview(row)
        }

        card.stack.addArrangedSubview(grid)
        return card
    }

    private func makeEvidenceCard() -> UIView {
        let card = sectionCard(index: 2, title: "上传现场证据")
        card.stack.addArrangedSubview(makeEvidenceUploadBlock(
            icon: "photo.fill",
            title: "上传图片",
            subtitle: "支持 JPG、PNG 格式，最多 9 张",
            attachments: viewModel.images,
            maxCount: 9,
            kind: .image
        ))
        card.stack.addArrangedSubview(makeDivider())
        card.stack.addArrangedSubview(makeEvidenceUploadBlock(
            icon: "video.fill",
            title: "上传视频",
            subtitle: "支持 MP4 格式，时长不超过 60 秒",
            attachments: viewModel.videos,
            maxCount: 3,
            kind: .video
        ))
        return card
    }

    private func makeDescriptionCard() -> UIView {
        let card = sectionCard(index: 3, title: "举报说明")
        descriptionView.text = viewModel.description
        updateDescriptionInputState()
        let inputContainer = UIView()
        inputContainer.backgroundColor = UIColor(hex: 0xFBFCFE)
        inputContainer.layer.cornerRadius = 12
        inputContainer.layer.borderWidth = 1
        inputContainer.layer.borderColor = UIColor(hex: 0xCDD5E1).cgColor

        inputContainer.addSubview(descriptionView)
        inputContainer.addSubview(descriptionPlaceholderLabel)
        inputContainer.addSubview(descriptionCountLabel)
        descriptionView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
            make.height.equalTo(132)
        }
        let textOriginX = descriptionView.textContainerInset.left
            + descriptionView.textContainer.lineFragmentPadding
        descriptionPlaceholderLabel.snp.makeConstraints { make in
            make.top.equalToSuperview().offset(descriptionView.textContainerInset.top)
            make.leading.equalToSuperview().offset(textOriginX)
            make.trailing.lessThanOrEqualToSuperview().inset(14)
        }
        descriptionCountLabel.snp.makeConstraints { make in
            make.trailing.bottom.equalToSuperview().inset(12)
        }
        card.stack.addArrangedSubview(inputContainer)
        return card
    }

    private func makeLocationCard() -> UIView {
        let card = sectionCard(index: 4, title: "举报位置")
        let container = UIView()
        container.backgroundColor = UIColor(hex: 0xF6FAFF)
        container.layer.cornerRadius = 12
        container.layer.borderWidth = 1
        container.layer.borderColor = UIColor(hex: 0xC8DDFF).cgColor

        let iconWrap = UIView()
        iconWrap.backgroundColor = AppColor.primary.withAlphaComponent(0.10)
        iconWrap.layer.cornerRadius = 21
        let icon = UIImageView(image: UIImage(systemName: "mappin.circle.fill"))
        icon.tintColor = AppColor.primary
        iconWrap.addSubview(icon)
        icon.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.size.equalTo(30)
        }

        let textStack = UIStackView()
        textStack.axis = .vertical
        textStack.spacing = 5
        let title = WildReportUI.label(viewModel.locationTitleText, font: .systemFont(ofSize: 15, weight: .semibold), color: AppColor.textPrimary, lines: 2)
        title.adjustsFontSizeToFitWidth = true
        title.minimumScaleFactor = 0.78
        textStack.addArrangedSubview(title)

        let button = UIButton(type: .system)
        button.setTitle(viewModel.isUpdatingLocation ? "定位中" : "更新定位", for: .normal)
        button.titleLabel?.font = .systemFont(ofSize: 13, weight: .semibold)
        button.setTitleColor(AppColor.primary, for: .normal)
        button.backgroundColor = .white
        button.layer.cornerRadius = 17
        button.layer.borderWidth = 1
        button.layer.borderColor = AppColor.primary.cgColor
        button.isEnabled = !viewModel.isUpdatingLocation
        button.alpha = viewModel.isUpdatingLocation ? 0.6 : 1
        button.addTarget(self, action: #selector(refreshLocationTapped), for: .touchUpInside)

        container.addSubview(iconWrap)
        container.addSubview(textStack)
        container.addSubview(button)
        iconWrap.snp.makeConstraints { make in
            make.leading.top.bottom.equalToSuperview().inset(12)
            make.size.equalTo(42)
        }
        button.snp.makeConstraints { make in
            make.trailing.equalToSuperview().inset(12)
            make.centerY.equalToSuperview()
            make.width.equalTo(82)
            make.height.equalTo(34)
        }
        textStack.snp.makeConstraints { make in
            make.leading.equalTo(iconWrap.snp.trailing).offset(12)
            make.trailing.equalTo(button.snp.leading).offset(-8)
            make.centerY.equalToSuperview()
            make.top.greaterThanOrEqualToSuperview().offset(12)
            make.bottom.lessThanOrEqualToSuperview().inset(12)
        }
        card.stack.addArrangedSubview(container)
        return card
    }

    private func makeContactCard() -> UIView {
        let card = sectionCard(index: 5, title: "摄影师微信号/手机号", optional: true)
        contactField.text = viewModel.contact
        card.stack.addArrangedSubview(contactField)
        contactField.snp.makeConstraints { make in make.height.equalTo(48) }
        return card
    }

    private func updateDescriptionInputState() {
        descriptionPlaceholderLabel.isHidden = !viewModel.description.isEmpty
        descriptionCountLabel.text = "\(viewModel.description.count)/500"
    }

    private func makePaymentCard() -> UIView {
        let card = sectionCard(index: 6, title: "线下微信、支付宝截图", optional: true)
        card.stack.addArrangedSubview(makeEvidenceUploadBlock(
            icon: "creditcard.fill",
            title: "上传支付截图",
            subtitle: "支持 JPG、PNG 格式，最多 3 张",
            attachments: viewModel.paymentImages,
            maxCount: 3,
            kind: .payment
        ))
        return card
    }

    private func sectionCard(index: Int, title: String, optional: Bool = false) -> WildReportCardView {
        let card = WildReportCardView(spacing: 18, inset: 16)
        card.layer.cornerRadius = 16
        card.stack.addArrangedSubview(WildReportSectionTitleView(index: index, title: title, optional: optional))
        return card
    }

    private func makeEvidenceUploadBlock(
        icon: String,
        title: String,
        subtitle: String,
        attachments: [WildReportAttachment],
        maxCount: Int,
        kind: WildReportAttachmentKind
    ) -> UIView {
        let block = UIStackView()
        block.axis = .vertical
        block.spacing = 12

        let header = UIStackView()
        header.axis = .horizontal
        header.alignment = .center
        header.spacing = 12

        let iconWrap = UIView()
        iconWrap.backgroundColor = AppColor.primaryLight
        iconWrap.layer.cornerRadius = 12
        let image = UIImageView(image: UIImage(systemName: icon))
        image.tintColor = AppColor.primary
        iconWrap.addSubview(image)
        image.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.size.equalTo(22)
        }
        iconWrap.snp.makeConstraints { make in
            make.size.equalTo(42)
        }

        let textStack = UIStackView()
        textStack.axis = .vertical
        textStack.spacing = 3
        textStack.addArrangedSubview(WildReportUI.label(title, font: .systemFont(ofSize: 16, weight: .bold), color: AppColor.textPrimary))
        textStack.addArrangedSubview(WildReportUI.label(subtitle, font: .systemFont(ofSize: 13), color: AppColor.textSecondary, lines: 0))

        header.addArrangedSubview(iconWrap)
        header.addArrangedSubview(textStack)
        header.addArrangedSubview(UIView())
        block.addArrangedSubview(header)

        let scroll = UIScrollView()
        scroll.keyboardDismissMode = .onDrag
        scroll.showsHorizontalScrollIndicator = false
        let tiles = UIStackView()
        tiles.axis = .horizontal
        tiles.spacing = 10
        scroll.addSubview(tiles)
        tiles.snp.makeConstraints { make in
            make.edges.equalTo(scroll.contentLayoutGuide).inset(UIEdgeInsets(top: 2, left: 0, bottom: 2, right: 0))
            make.height.equalTo(scroll.frameLayoutGuide).offset(-4)
        }
        attachments.forEach { attachment in
            tiles.addArrangedSubview(WildReportAttachmentTileView(
                attachment: attachment,
                onPreview: { [weak self] in self?.previewAttachment(attachment) },
                onRemove: { [weak self] in self?.viewModel.removeAttachment(attachment) }
            ))
        }
        let addCount = availableAddTileCount(kind: kind, attachments: attachments, maxCount: maxCount)
        for _ in 0..<addCount {
            let addTile = WildReportAddAttachmentTileView { [weak self] in
                self?.presentMediaPicker(for: WildReportPickerTarget(kind: kind))
            }
            tiles.addArrangedSubview(addTile)
        }
        scroll.snp.makeConstraints { make in
            make.height.equalTo(62)
        }
        block.addArrangedSubview(scroll)
        return block
    }

    private func makeDivider() -> UIView {
        let divider = UIView()
        divider.backgroundColor = AppColor.border
        divider.snp.makeConstraints { make in
            make.height.equalTo(1)
        }
        return divider
    }

    private func realNameBannerText() -> NSAttributedString {
        let text = NSMutableAttributedString()
        [
            ("实名登录", AppColor.primary, UIFont.systemFont(ofSize: 16, weight: .bold)),
            ("后提交，", AppColor.textPrimary, UIFont.systemFont(ofSize: 16, weight: .medium)),
            ("景区公安", AppColor.primary, UIFont.systemFont(ofSize: 16, weight: .bold)),
            ("将接收并处理", AppColor.textPrimary, UIFont.systemFont(ofSize: 16, weight: .medium))
        ].forEach { value, color, font in
            text.append(NSAttributedString(string: value, attributes: [
                .foregroundColor: color,
                .font: font
            ]))
        }
        let paragraph = NSMutableParagraphStyle()
        paragraph.lineSpacing = 4
        text.addAttribute(.paragraphStyle, value: paragraph, range: NSRange(location: 0, length: text.length))
        return text
    }

    private func presentMediaPicker(for target: WildReportPickerTarget) {
        dismissKeyboard()
        let remaining = remainingSelectionCount(for: target)
        guard remaining > 0 else {
            showToast(target.limitMessage)
            return
        }
        pickerTarget = target
        var configuration = PHPickerConfiguration(photoLibrary: .shared())
        configuration.selectionLimit = remaining
        configuration.filter = target == .video ? .videos : .images
        let picker = PHPickerViewController(configuration: configuration)
        picker.delegate = self
        present(picker, animated: true)
    }

    private func remainingSelectionCount(for target: WildReportPickerTarget) -> Int {
        switch target {
        case .image:
            return max(0, 9 - viewModel.images.count - viewModel.videos.count)
        case .video:
            return max(0, min(3 - viewModel.videos.count, 9 - viewModel.images.count - viewModel.videos.count))
        case .payment:
            return max(0, 3 - viewModel.paymentImages.count)
        }
    }

    private func availableAddTileCount(
        kind: WildReportAttachmentKind,
        attachments: [WildReportAttachment],
        maxCount: Int
    ) -> Int {
        let ownRemaining = max(0, maxCount - attachments.count)
        let evidenceRemaining = max(0, 9 - viewModel.images.count - viewModel.videos.count)
        switch kind {
        case .image:
            return min(ownRemaining, evidenceRemaining) > 0 ? 1 : 0
        case .video:
            return min(ownRemaining, evidenceRemaining) > 0 ? 1 : 0
        case .payment:
            return ownRemaining > 0 ? 1 : 0
        }
    }

    private func previewAttachment(_ attachment: WildReportAttachment) {
        guard let item = MediaPreviewItem(wildReportAttachment: attachment) else {
            showToast("当前附件暂无本地预览")
            return
        }
        presentMediaPreview(items: [item], startIndex: 0)
    }

    @objc private func refreshLocationTapped() { viewModel.refreshCurrentLocation() }
    @objc private func submitTapped() {
        Task {
            await viewModel.submitReport(
                api: api,
                uploader: uploader,
                scenicId: AppStore.shared.session.currentScenicId,
                scenicName: AppStore.shared.session.currentScenicName
            )
        }
    }
    @objc private func contactChanged() {
        viewModel.updateContact(contactField.text ?? "")
        guard contactField.markedTextRange == nil,
              contactField.text != viewModel.contact else { return }
        contactField.text = viewModel.contact
    }
    @objc private func dismissKeyboard() { view.endEditing(true) }

    @MainActor
    private func updateSubmitLoading() {
        if viewModel.isSubmitting {
            guard !isShowingSubmitLoading else { return }
            isShowingSubmitLoading = true
            showLoading()
        } else if isShowingSubmitLoading {
            isShowingSubmitLoading = false
            hideLoading()
        }
    }

    private func registerKeyboardNotifications() {
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(keyboardWillChangeFrame(_:)),
            name: UIResponder.keyboardWillChangeFrameNotification,
            object: nil
        )
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(keyboardWillHide(_:)),
            name: UIResponder.keyboardWillHideNotification,
            object: nil
        )
    }

    @objc private func keyboardWillChangeFrame(_ notification: Notification) {
        guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }
        let keyboardFrameInView = view.convert(keyboardFrame, from: nil)
        let overlap = max(0, view.bounds.maxY - keyboardFrameInView.minY)
        updateScrollInsets(bottom: overlap + 16, notification: notification)
        scrollActiveInputIntoView(animated: true)
    }

    @objc private func keyboardWillHide(_ notification: Notification) {
        updateScrollInsets(bottom: 0, notification: notification)
    }

    private func updateScrollInsets(bottom: CGFloat, notification: Notification) {
        let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval ?? 0.25
        let curveRaw = notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt ?? UIView.AnimationOptions.curveEaseInOut.rawValue
        let options = UIView.AnimationOptions(rawValue: curveRaw << 16)
        UIView.animate(withDuration: duration, delay: 0, options: options) {
            self.scrollView.contentInset.bottom = bottom
            self.scrollView.verticalScrollIndicatorInsets.bottom = bottom
        }
    }

    private func scrollActiveInputIntoView(animated: Bool) {
        guard let activeInputView else { return }
        view.layoutIfNeeded()
        let rect = activeInputView.convert(activeInputView.bounds, to: scrollView).insetBy(dx: 0, dy: -24)
        scrollView.scrollRectToVisible(rect, animated: animated)
    }
}

extension WildPhotographerReportSubmitViewController: PHPickerViewControllerDelegate {
    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        picker.dismiss(animated: true)
        guard !results.isEmpty else { return }
        let target = pickerTarget
        Task {
            do {
                let attachments = try await WildReportLocalMediaLoader.load(results: results, target: target)
                await MainActor.run {
                    switch target {
                    case .image:
                        viewModel.addImages(attachments)
                    case .video:
                        viewModel.addVideos(attachments)
                    case .payment:
                        viewModel.addPaymentImages(attachments)
                    }
                }
            } catch {
                await MainActor.run { showToast(error.localizedDescription) }
            }
        }
    }
}

extension WildPhotographerReportSubmitViewController: UITextViewDelegate {
    func textViewDidBeginEditing(_ textView: UITextView) {
        activeInputView = textView
        DispatchQueue.main.async { [weak self] in self?.scrollActiveInputIntoView(animated: true) }
    }

    func textViewDidChange(_ textView: UITextView) {
        viewModel.updateDescription(textView.text)
        if textView.markedTextRange == nil, textView.text != viewModel.description {
            textView.text = viewModel.description
        }
        updateDescriptionInputState()
    }

    func textViewDidEndEditing(_ textView: UITextView) {
        if activeInputView === textView {
            activeInputView = nil
        }
    }
}

extension WildPhotographerReportSubmitViewController: UITextFieldDelegate {
    func textFieldDidBeginEditing(_ textField: UITextField) {
        activeInputView = textField
        DispatchQueue.main.async { [weak self] in self?.scrollActiveInputIntoView(animated: true) }
    }

    func textFieldDidEndEditing(_ textField: UITextField) {
        if activeInputView === textField {
            activeInputView = nil
        }
    }
}

extension WildPhotographerReportSubmitViewController: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        var touchedView = touch.view
        while let currentView = touchedView {
            if currentView is UIControl || currentView is UITextView || currentView is UITextField {
                return false
            }
            touchedView = currentView.superview
        }
        return true
    }
}

/// 提交页步骤标题，复刻参考页左侧蓝色竖条与选填标识。
private final class WildReportSectionTitleView: UIView {
    init(index: Int, title: String, optional: Bool) {
        super.init(frame: .zero)
        let bar = UIView()
        bar.backgroundColor = AppColor.primary
        bar.layer.cornerRadius = 2

        let label = UILabel()
        label.font = .systemFont(ofSize: 17, weight: .bold)
        label.textColor = AppColor.textPrimary
        label.text = "\(index). \(title)"

        let optionalLabel = UILabel()
        optionalLabel.font = .systemFont(ofSize: 14)
        optionalLabel.textColor = AppColor.textSecondary
        optionalLabel.text = "（选填）"
        optionalLabel.isHidden = !optional

        addSubview(bar)
        addSubview(label)
        addSubview(optionalLabel)
        bar.snp.makeConstraints { make in
            make.leading.equalToSuperview()
            make.centerY.equalToSuperview()
            make.width.equalTo(4)
            make.height.equalTo(20)
        }
        label.snp.makeConstraints { make in
            make.leading.equalTo(bar.snp.trailing).offset(8)
            make.top.bottom.equalToSuperview()
        }
        optionalLabel.snp.makeConstraints { make in
            make.leading.equalTo(label.snp.trailing)
            make.centerY.equalTo(label)
            make.trailing.lessThanOrEqualToSuperview()
        }
    }

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

/// 提交页举报类型三列选择按钮。
private final class WildReportTypeOptionButton: UIControl {
    private let titleLabel = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)
        layer.cornerRadius = 12
        layer.borderWidth = 1

        titleLabel.font = .systemFont(ofSize: 15, weight: .bold)
        titleLabel.textAlignment = .center
        titleLabel.adjustsFontSizeToFitWidth = true
        titleLabel.minimumScaleFactor = 0.8
        titleLabel.numberOfLines = 2
        titleLabel.lineBreakMode = .byTruncatingTail
        titleLabel.isUserInteractionEnabled = false

        addSubview(titleLabel)
        titleLabel.snp.makeConstraints { make in
            make.centerY.equalToSuperview()
            make.leading.trailing.equalToSuperview().inset(6)
        }
    }

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

    func apply(type: WildReportType, isSelected: Bool) {
        titleLabel.text = type.label
        backgroundColor = isSelected ? AppColor.primary : UIColor(hex: 0xF6FAFF)
        layer.borderColor = (isSelected ? AppColor.primary : UIColor(hex: 0xC8DDFF)).cgColor
        titleLabel.textColor = isSelected ? .white : AppColor.textPrimary
    }
}

/// 举报成功页面，展示编号、上传摘要和处理进度。
final class WildPhotographerReportSuccessViewController: BaseViewController {
    private let viewModel: WildPhotographerReportSuccessViewModel
    private let homeViewModel: WildPhotographerReportHomeViewModel
    private let scrollView = UIScrollView()
    private let stack = UIStackView()

    init(result: WildReportSubmitResult, homeViewModel: WildPhotographerReportHomeViewModel) {
        self.viewModel = WildPhotographerReportSuccessViewModel(result: result)
        self.homeViewModel = homeViewModel
        super.init(nibName: nil, bundle: nil)
    }

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

    override func setupNavigationBar() {
        title = "举报成功"
    }

    override func setupUI() {
        view.backgroundColor = UIColor(hex: 0xF8FAFF)
        scrollView.showsVerticalScrollIndicator = false
        stack.axis = .vertical
        stack.spacing = 12
        view.addSubview(scrollView)
        scrollView.addSubview(stack)

        stack.addArrangedSubview(WildReportSuccessHeroView())
        stack.addArrangedSubview(WildReportSuccessProgressView(steps: viewModel.progressSteps))

        let detailCard = WildReportSuccessDetailCardView(
            reportID: viewModel.result.record.id,
            reportType: viewModel.result.record.reportType.label,
            location: viewModel.result.record.location,
            uploadSummary: viewModel.uploadSummary
        )
        detailCard.onCopyReportID = { [weak self] in
            guard let self else { return }
            _ = viewModel.copyReportID()
            showToast("举报编号已复制")
        }
        stack.addArrangedSubview(detailCard)

        let detailButton = makeActionButton(title: "查看处理进度", style: .primary)
        detailButton.addTarget(self, action: #selector(detailTapped), for: .touchUpInside)
        let homeButton = makeActionButton(title: "返回首页", style: .secondary)
        homeButton.addTarget(self, action: #selector(homeTapped), for: .touchUpInside)
        stack.addArrangedSubview(detailButton)
        stack.addArrangedSubview(homeButton)
    }

    override func setupConstraints() {
        scrollView.snp.makeConstraints { make in
            make.edges.equalTo(view.safeAreaLayoutGuide)
        }
        stack.snp.makeConstraints { make in
            make.top.equalToSuperview().offset(14)
            make.leading.trailing.equalTo(scrollView.frameLayoutGuide).inset(18)
            make.bottom.equalToSuperview().inset(28)
            make.width.equalTo(scrollView.frameLayoutGuide).offset(-36)
        }
    }

    private func makeActionButton(title: String, style: WildReportSuccessActionButtonStyle) -> UIButton {
        let button = UIButton(type: .system)
        button.titleLabel?.font = .systemFont(ofSize: 20, weight: .bold)
        button.setTitle(title, for: .normal)
        button.layer.cornerRadius = 14
        button.layer.borderWidth = style == .secondary ? 1.2 : 0
        button.layer.borderColor = AppColor.primary.cgColor
        button.backgroundColor = style == .primary ? AppColor.primary : .white
        button.setTitleColor(style == .primary ? .white : AppColor.primary, for: .normal)
        button.snp.makeConstraints { make in
            make.height.equalTo(54)
        }
        return button
    }

    @objc private func detailTapped() {
        navigationController?.pushViewController(
            WildPhotographerReportDetailViewController(record: viewModel.result.record, homeViewModel: homeViewModel),
            animated: true
        )
    }

    @objc private func homeTapped() {
        navigationController?.popToViewController(
            navigationController?.viewControllers.first(where: { $0 is WildPhotographerReportHomeViewController }) ?? self,
            animated: true
        )
    }
}

/// 举报成功页底部操作按钮样式。
private enum WildReportSuccessActionButtonStyle {
    case primary
    case secondary
}

/// 举报成功页顶部渐变成功反馈区。
private final class WildReportSuccessHeroView: UIView {
    private let gradientLayer = CAGradientLayer()
    private let stack = UIStackView()

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

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

    override func layoutSubviews() {
        super.layoutSubviews()
        gradientLayer.frame = bounds
        gradientLayer.cornerRadius = 18
    }

    private func setupUI() {
        layer.cornerRadius = 18
        layer.borderWidth = 1
        layer.borderColor = UIColor(hex: 0xBFD7FF).cgColor
        layer.masksToBounds = true

        gradientLayer.colors = [
            UIColor.white.cgColor,
            UIColor(hex: 0xEEF6FF).cgColor
        ]
        gradientLayer.startPoint = CGPoint(x: 0, y: 0)
        gradientLayer.endPoint = CGPoint(x: 1, y: 1)
        layer.insertSublayer(gradientLayer, at: 0)

        stack.axis = .vertical
        stack.alignment = .center
        stack.spacing = 14

        let iconView = WildReportSuccessCheckmarkView()
        let titleLabel = WildReportUI.label("举报已提交", font: .systemFont(ofSize: 24, weight: .heavy), color: AppColor.textPrimary)
        let descriptionLabel = WildReportUI.label(
            "景区公安已收到线索，将尽快前往现场核实处理",
            font: .systemFont(ofSize: 14),
            color: AppColor.textSecondary,
            lines: 0
        )
        descriptionLabel.textAlignment = .center

        addSubview(stack)
        stack.addArrangedSubview(iconView)
        stack.addArrangedSubview(titleLabel)
        stack.addArrangedSubview(descriptionLabel)

        iconView.snp.makeConstraints { make in
            make.size.equalTo(78)
        }
        descriptionLabel.snp.makeConstraints { make in
            make.leading.trailing.equalToSuperview().inset(16)
        }
    }

    private func setupConstraints() {
        stack.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(UIEdgeInsets(top: 24, left: 16, bottom: 24, right: 16))
        }
    }
}

/// 举报成功页圆形对勾图标。
private final class WildReportSuccessCheckmarkView: UIView {
    private let outerCircle = UIView()
    private let innerCircle = UIView()
    private let iconView = UIImageView(image: UIImage(systemName: "checkmark"))

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

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

    private func setupUI() {
        outerCircle.backgroundColor = AppColor.primary.withAlphaComponent(0.12)
        outerCircle.layer.cornerRadius = 39
        innerCircle.backgroundColor = AppColor.primary
        innerCircle.layer.cornerRadius = 29
        iconView.tintColor = .white
        iconView.contentMode = .scaleAspectFit
        iconView.preferredSymbolConfiguration = UIImage.SymbolConfiguration(pointSize: 28, weight: .bold)

        addSubview(outerCircle)
        addSubview(innerCircle)
        addSubview(iconView)
    }

    private func setupConstraints() {
        outerCircle.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        innerCircle.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.size.equalTo(58)
        }
        iconView.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.size.equalTo(28)
        }
    }
}

/// 举报成功页横向处理进度卡片。
private final class WildReportSuccessProgressView: UIView {
    private let contentView = UIView()
    private let stack = UIStackView()
    private let steps: [String]
    private var itemViews: [WildReportSuccessProgressItemView] = []

    init(steps: [String]) {
        self.steps = steps
        super.init(frame: .zero)
        setupUI()
        setupConstraints()
    }

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

    private func setupUI() {
        backgroundColor = .white
        layer.cornerRadius = 16
        layer.borderWidth = 1
        layer.borderColor = AppColor.border.cgColor

        stack.axis = .horizontal
        stack.alignment = .top
        stack.distribution = .fillEqually
        stack.spacing = 0

        addSubview(contentView)
        contentView.addSubview(stack)
        for (index, step) in steps.enumerated() {
            let item = WildReportSuccessProgressItemView(title: step, isCompleted: index == 0)
            itemViews.append(item)
            stack.addArrangedSubview(item)
        }

        for index in steps.indices.dropLast() {
            if index < steps.count - 1 {
                let connector = WildReportSuccessProgressConnectorView(isCompleted: index == 0)
                contentView.insertSubview(connector, belowSubview: stack)
                connector.snp.makeConstraints { make in
                    make.leading.equalTo(itemViews[index].connectorAnchorView.snp.centerX).offset(12)
                    make.trailing.equalTo(itemViews[index + 1].connectorAnchorView.snp.centerX).offset(-12)
                    make.centerY.equalTo(itemViews[index].connectorAnchorView)
                    make.height.equalTo(24)
                }
            }
        }
    }

    private func setupConstraints() {
        contentView.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(UIEdgeInsets(top: 16, left: 12, bottom: 16, right: 12))
        }
        stack.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
    }
}

/// 举报成功页单个进度节点。
private final class WildReportSuccessProgressItemView: UIView {
    private let nodeView: WildReportSuccessProgressNodeView
    private let titleLabel = UILabel()

    var connectorAnchorView: UIView { nodeView }

    init(title: String, isCompleted: Bool) {
        self.nodeView = WildReportSuccessProgressNodeView(isCompleted: isCompleted)
        super.init(frame: .zero)
        setupUI(title: title, isCompleted: isCompleted)
        setupConstraints()
    }

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

    private func setupUI(title: String, isCompleted: Bool) {
        titleLabel.text = title
        titleLabel.font = .systemFont(ofSize: 11, weight: isCompleted ? .semibold : .regular)
        titleLabel.textColor = isCompleted ? AppColor.primary : UIColor(hex: 0x9CA3AF)
        titleLabel.textAlignment = .center
        titleLabel.numberOfLines = 2

        addSubview(nodeView)
        addSubview(titleLabel)
    }

    private func setupConstraints() {
        nodeView.snp.makeConstraints { make in
            make.top.centerX.equalToSuperview()
            make.size.equalTo(24)
        }
        titleLabel.snp.makeConstraints { make in
            make.top.equalTo(nodeView.snp.bottom).offset(8)
            make.leading.trailing.bottom.equalToSuperview()
        }
    }
}

/// 举报成功页进度节点圆点。
private final class WildReportSuccessProgressNodeView: UIView {
    private let innerCircle = UIView()
    private let checkmarkView = UIImageView(image: UIImage(systemName: "checkmark"))
    private let isCompleted: Bool

    init(isCompleted: Bool) {
        self.isCompleted = isCompleted
        super.init(frame: .zero)
        setupUI()
        setupConstraints()
    }

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

    private func setupUI() {
        layer.cornerRadius = 12
        layer.borderWidth = 2
        layer.borderColor = (isCompleted ? AppColor.primary : UIColor(hex: 0xD1D5DB)).cgColor

        innerCircle.backgroundColor = isCompleted ? AppColor.primary : .clear
        innerCircle.layer.cornerRadius = 10
        checkmarkView.tintColor = .white
        checkmarkView.contentMode = .scaleAspectFit
        checkmarkView.preferredSymbolConfiguration = UIImage.SymbolConfiguration(pointSize: 10, weight: .bold)
        checkmarkView.isHidden = !isCompleted

        addSubview(innerCircle)
        addSubview(checkmarkView)
    }

    private func setupConstraints() {
        innerCircle.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.size.equalTo(20)
        }
        checkmarkView.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.size.equalTo(10)
        }
    }
}

/// 举报成功页进度节点连接线。
private final class WildReportSuccessProgressConnectorView: UIView {
    private let isCompleted: Bool
    private let solidLine = UIView()
    private let dotsStack = UIStackView()

    init(isCompleted: Bool) {
        self.isCompleted = isCompleted
        super.init(frame: .zero)
        setupUI()
        setupConstraints()
    }

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

    private func setupUI() {
        if isCompleted {
            solidLine.backgroundColor = AppColor.primary
            addSubview(solidLine)
        } else {
            dotsStack.axis = .horizontal
            dotsStack.alignment = .center
            dotsStack.distribution = .equalSpacing
            dotsStack.spacing = 3
            addSubview(dotsStack)
            for _ in 0..<5 {
                let dot = UIView()
                dot.backgroundColor = UIColor(hex: 0xD1D5DB)
                dot.layer.cornerRadius = 1.5
                dot.snp.makeConstraints { make in
                    make.size.equalTo(3)
                }
                dotsStack.addArrangedSubview(dot)
            }
        }
    }

    private func setupConstraints() {
        snp.makeConstraints { make in
            make.height.equalTo(24)
        }
        if isCompleted {
            solidLine.snp.makeConstraints { make in
                make.leading.trailing.equalToSuperview()
                make.centerY.equalToSuperview()
                make.height.equalTo(2)
            }
        } else {
            dotsStack.snp.makeConstraints { make in
                make.centerY.equalToSuperview()
                make.leading.trailing.equalToSuperview()
            }
        }
    }
}

/// 举报成功页详情信息卡。
private final class WildReportSuccessDetailCardView: UIView {
    var onCopyReportID: (() -> Void)?
    private let stack = UIStackView()

    init(reportID: String, reportType: String, location: String, uploadSummary: String) {
        super.init(frame: .zero)
        setupUI(reportID: reportID, reportType: reportType, location: location, uploadSummary: uploadSummary)
        setupConstraints()
    }

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

    private func setupUI(reportID: String, reportType: String, location: String, uploadSummary: String) {
        backgroundColor = .white
        layer.cornerRadius = 16
        layer.borderWidth = 1
        layer.borderColor = AppColor.border.cgColor

        stack.axis = .vertical
        stack.spacing = 0
        addSubview(stack)

        appendRow(icon: "doc.text.fill", title: "举报编号", value: reportID, valueColor: AppColor.primary, showsCopy: true)
        appendDivider()
        appendRow(icon: "tag.fill", title: "举报类型", value: reportType)
        appendDivider()
        appendRow(icon: "mappin.circle.fill", title: "举报位置", value: location)
        appendDivider()
        appendRow(icon: "photo.on.rectangle.angled", title: "已上传", value: uploadSummary)
    }

    private func setupConstraints() {
        stack.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
    }

    private func appendRow(icon: String, title: String, value: String, valueColor: UIColor = AppColor.textPrimary, showsCopy: Bool = false) {
        let row = WildReportSuccessDetailRowView(
            icon: icon,
            title: title,
            value: value,
            valueColor: valueColor,
            showsCopy: showsCopy
        )
        row.onCopy = { [weak self] in self?.onCopyReportID?() }
        stack.addArrangedSubview(row)
    }

    private func appendDivider() {
        let container = UIView()
        let divider = UIView()
        divider.backgroundColor = AppColor.border
        container.addSubview(divider)
        divider.snp.makeConstraints { make in
            make.top.bottom.trailing.equalToSuperview()
            make.leading.equalToSuperview().offset(56)
            make.height.equalTo(1)
        }
        stack.addArrangedSubview(container)
    }
}

/// 举报成功页详情信息行。
private final class WildReportSuccessDetailRowView: UIView {
    var onCopy: (() -> Void)?
    private let iconContainer = UIView()
    private let iconView = UIImageView()
    private let titleLabel = UILabel()
    private let valueLabel = UILabel()
    private let copyButton = UIButton(type: .system)
    private let showsCopy: Bool

    init(icon: String, title: String, value: String, valueColor: UIColor, showsCopy: Bool) {
        self.showsCopy = showsCopy
        super.init(frame: .zero)
        setupUI(icon: icon, title: title, value: value, valueColor: valueColor)
        setupConstraints()
    }

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

    private func setupUI(icon: String, title: String, value: String, valueColor: UIColor) {
        iconContainer.backgroundColor = AppColor.primaryLight
        iconContainer.layer.cornerRadius = 10
        iconView.image = UIImage(systemName: icon)
        iconView.tintColor = AppColor.primary
        iconView.contentMode = .scaleAspectFit
        iconView.preferredSymbolConfiguration = UIImage.SymbolConfiguration(pointSize: 22, weight: .semibold)

        titleLabel.text = title
        titleLabel.font = .systemFont(ofSize: 13)
        titleLabel.textColor = AppColor.textSecondary

        valueLabel.text = value
        valueLabel.font = .systemFont(ofSize: 16, weight: .semibold)
        valueLabel.textColor = valueColor
        valueLabel.numberOfLines = 2
        valueLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)

        addSubview(iconContainer)
        iconContainer.addSubview(iconView)
        addSubview(titleLabel)
        addSubview(valueLabel)

        guard showsCopy else { return }
        copyButton.setImage(UIImage(systemName: "doc.on.doc"), for: .normal)
        copyButton.tintColor = AppColor.primary
        copyButton.backgroundColor = AppColor.primaryLight
        copyButton.layer.cornerRadius = 17
        copyButton.accessibilityLabel = "复制举报编号"
        copyButton.addTarget(self, action: #selector(copyTapped), for: .touchUpInside)
        addSubview(copyButton)
    }

    private func setupConstraints() {
        iconContainer.snp.makeConstraints { make in
            make.leading.equalToSuperview().offset(16)
            make.centerY.equalToSuperview()
            make.size.equalTo(42)
            make.top.greaterThanOrEqualToSuperview().offset(14)
            make.bottom.lessThanOrEqualToSuperview().inset(14)
        }
        iconView.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.size.equalTo(22)
        }
        titleLabel.snp.makeConstraints { make in
            make.top.equalToSuperview().offset(14)
            make.leading.equalTo(iconContainer.snp.trailing).offset(14)
            make.trailing.lessThanOrEqualToSuperview().inset(16)
        }
        valueLabel.snp.makeConstraints { make in
            make.top.equalTo(titleLabel.snp.bottom).offset(4)
            make.leading.equalTo(titleLabel)
            make.bottom.equalToSuperview().inset(14)
            if showsCopy {
                make.trailing.lessThanOrEqualTo(copyButton.snp.leading).offset(-8)
            } else {
                make.trailing.equalToSuperview().inset(16)
            }
        }

        guard showsCopy else { return }
        copyButton.snp.makeConstraints { make in
            make.trailing.equalToSuperview().inset(16)
            make.centerY.equalToSuperview()
            make.size.equalTo(34)
        }
    }

    @objc private func copyTapped() {
        onCopy?()
    }
}
