//
//  OrderCodeScannerView.swift
//  suixinkan
//
//  Created by Codex on 2026/6/22.
//

import AVFoundation
import SwiftUI

/// 订单扫码错误实体，描述相机不可用、权限拒绝和元数据异常。
enum OrderScannerError: LocalizedError {
    case cameraUnavailable
    case permissionDenied
    case invalidMetadata

    var errorDescription: String? {
        switch self {
        case .cameraUnavailable:
            return "当前设备不可用相机，无法扫码。"
        case .permissionDenied:
            return "未开启相机权限，请在系统设置中允许后重试。"
        case .invalidMetadata:
            return "未识别到有效条码，请重试。"
        }
    }
}

/// SwiftUI 扫码页包装，提供标题、关闭按钮和底部扫码提示。
struct OrderScannerPage: View {
    let onClose: () -> Void
    let onSuccess: (String) -> Void
    let onFailure: (Error) -> Void

    var body: some View {
        NavigationStack {
            ZStack(alignment: .bottom) {
                OrderCodeScannerView { result in
                    switch result {
                    case .success(let raw):
                        onSuccess(raw)
                    case .failure(let error):
                        onFailure(error)
                    }
                }
                .ignoresSafeArea()

                Label("请将二维码/条码放入取景框", systemImage: "qrcode.viewfinder")
                    .font(.system(size: AppMetrics.FontSize.footnote, weight: .medium))
                    .foregroundStyle(.white)
                    .padding(.horizontal, AppMetrics.Spacing.medium)
                    .padding(.vertical, AppMetrics.Spacing.xSmall)
                    .background(.black.opacity(0.55), in: Capsule())
                    .padding(.bottom, AppMetrics.Spacing.xxLarge)
            }
            .navigationTitle("扫码核销")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("关闭") {
                        onClose()
                    }
                    .accessibilityIdentifier("scanner.close")
                }
            }
        }
    }
}

/// AVFoundation 扫码桥接视图，负责把相机识别结果回传给 SwiftUI。
struct OrderCodeScannerView: UIViewControllerRepresentable {
    let onScanResult: (Result<String, Error>) -> Void

    /// 创建底层扫码控制器。
    func makeUIViewController(context: Context) -> OrderScannerViewController {
        let controller = OrderScannerViewController()
        controller.onScanResult = onScanResult
        return controller
    }

    /// 扫码控制器没有 SwiftUI 驱动的可变配置。
    func updateUIViewController(_ uiViewController: OrderScannerViewController, context: Context) {}
}

/// AVFoundation 扫码控制器，管理相机权限、预览层、识别类型和手电筒。
final class OrderScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
    var onScanResult: ((Result<String, Error>) -> Void)?

    private let session = AVCaptureSession()
    private var previewLayer: AVCaptureVideoPreviewLayer?
    private var videoDevice: AVCaptureDevice?
    private var hasFinishedScan = false

    private let scanFrameView: UIView = {
        let view = UIView()
        view.layer.borderColor = UIColor.systemBlue.cgColor
        view.layer.borderWidth = 2
        view.layer.cornerRadius = 12
        view.backgroundColor = .clear
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    private let guideLabel: UILabel = {
        let label = UILabel()
        label.text = "将二维码/条形码放入框内自动识别"
        label.textColor = .white
        label.font = .systemFont(ofSize: 13, weight: .medium)
        label.textAlignment = .center
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    private lazy var torchButton: UIButton = {
        let button = Self.makeScannerButton(title: "手电筒", backgroundColor: UIColor.black.withAlphaComponent(0.42))
        button.addTarget(self, action: #selector(toggleTorch), for: .touchUpInside)
        return button
    }()

    private lazy var retryButton: UIButton = {
        let button = Self.makeScannerButton(title: "重新扫描", backgroundColor: UIColor.systemBlue.withAlphaComponent(0.85))
        button.addTarget(self, action: #selector(restartScan), for: .touchUpInside)
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .black
        setupControls()
        checkPermissionAndStart()
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        previewLayer?.frame = view.layer.bounds
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        updateTorch(enabled: false)
        stopSession()
    }

    /// 检查相机权限，并在授权后启动扫码。
    private func checkPermissionAndStart() {
        switch AVCaptureDevice.authorizationStatus(for: .video) {
        case .authorized:
            startSession()
        case .notDetermined:
            AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
                DispatchQueue.main.async {
                    guard let self else { return }
                    if granted {
                        self.startSession()
                    } else {
                        self.onScanResult?(.failure(OrderScannerError.permissionDenied))
                    }
                }
            }
        case .denied, .restricted:
            onScanResult?(.failure(OrderScannerError.permissionDenied))
        @unknown default:
            onScanResult?(.failure(OrderScannerError.permissionDenied))
        }
    }

    /// 启动扫码会话。
    private func startSession() {
        hasFinishedScan = false
        guard !session.isRunning else { return }

        do {
            try configureSession()
        } catch {
            onScanResult?(.failure(error))
            return
        }

        DispatchQueue.global(qos: .userInitiated).async { [weak self] in
            self?.session.startRunning()
        }
    }

    /// 停止扫码会话。
    private func stopSession() {
        guard session.isRunning else { return }
        session.stopRunning()
    }

    /// 创建扫码页底部操作按钮。
    private static func makeScannerButton(title: String, backgroundColor: UIColor) -> UIButton {
        var attributedTitle = AttributedString(title)
        attributedTitle.font = .systemFont(ofSize: 13, weight: .semibold)

        var configuration = UIButton.Configuration.filled()
        configuration.attributedTitle = attributedTitle
        configuration.baseForegroundColor = .white
        configuration.baseBackgroundColor = backgroundColor
        configuration.contentInsets = NSDirectionalEdgeInsets(top: 8, leading: 12, bottom: 8, trailing: 12)
        configuration.cornerStyle = .capsule

        let button = UIButton(type: .system)
        button.configuration = configuration
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }

    /// 配置相机输入、元数据输出和预览层。
    private func configureSession() throws {
        if previewLayer != nil {
            return
        }

        guard let videoDevice = AVCaptureDevice.default(for: .video) else {
            throw OrderScannerError.cameraUnavailable
        }
        self.videoDevice = videoDevice

        let videoInput = try AVCaptureDeviceInput(device: videoDevice)
        guard session.canAddInput(videoInput) else {
            throw OrderScannerError.cameraUnavailable
        }
        session.addInput(videoInput)

        let metadataOutput = AVCaptureMetadataOutput()
        guard session.canAddOutput(metadataOutput) else {
            throw OrderScannerError.invalidMetadata
        }
        session.addOutput(metadataOutput)

        metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
        metadataOutput.metadataObjectTypes = [
            .qr,
            .ean8,
            .ean13,
            .pdf417,
            .code39,
            .code93,
            .code128,
            .dataMatrix,
            .aztec,
            .itf14,
            .upce
        ]

        let preview = AVCaptureVideoPreviewLayer(session: session)
        preview.videoGravity = .resizeAspectFill
        preview.frame = view.layer.bounds
        view.layer.insertSublayer(preview, at: 0)
        previewLayer = preview
        updateTorchButtonState()
    }

    /// 处理相机识别到的二维码或条形码内容。
    func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
        guard !hasFinishedScan else { return }

        guard let metadataObject = metadataObjects.first as? AVMetadataMachineReadableCodeObject,
              let code = metadataObject.stringValue,
              !code.isEmpty else {
            return
        }

        hasFinishedScan = true
        stopSession()
        UINotificationFeedbackGenerator().notificationOccurred(.success)
        onScanResult?(.success(code))
    }

    /// 布局扫码框、提示文本和操作按钮。
    private func setupControls() {
        view.addSubview(scanFrameView)
        view.addSubview(guideLabel)
        view.addSubview(torchButton)
        view.addSubview(retryButton)

        NSLayoutConstraint.activate([
            scanFrameView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            scanFrameView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -40),
            scanFrameView.widthAnchor.constraint(equalToConstant: 240),
            scanFrameView.heightAnchor.constraint(equalToConstant: 240),
            guideLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 24),
            guideLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -24),
            guideLabel.topAnchor.constraint(equalTo: scanFrameView.bottomAnchor, constant: 14),
            torchButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16),
            torchButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -18),
            retryButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16),
            retryButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -18)
        ])
    }

    /// 根据设备能力更新手电筒按钮状态。
    private func updateTorchButtonState() {
        let hasTorch = videoDevice?.hasTorch == true
        torchButton.isEnabled = hasTorch
        torchButton.alpha = hasTorch ? 1 : 0.45
    }

    /// 切换手电筒开关。
    @objc
    private func toggleTorch() {
        let next = !(videoDevice?.isTorchActive ?? false)
        updateTorch(enabled: next)
    }

    /// 设置手电筒状态。
    private func updateTorch(enabled: Bool) {
        guard let device = videoDevice, device.hasTorch else { return }
        do {
            try device.lockForConfiguration()
            if enabled {
                try device.setTorchModeOn(level: AVCaptureDevice.maxAvailableTorchLevel)
            } else {
                device.torchMode = .off
            }
            device.unlockForConfiguration()
            torchButton.setTitle(enabled ? "关闭手电" : "手电筒", for: .normal)
        } catch {
            torchButton.setTitle("手电筒不可用", for: .normal)
        }
    }

    /// 重置扫描状态并重新启动识别。
    @objc
    private func restartScan() {
        hasFinishedScan = false
        updateTorch(enabled: false)
        startSession()
    }
}
