import SwiftUI
import AVFoundation
import UIKit

/// 指标速记 · 静态图框选识别。
/// 拍/选一张后,在静态照片上手动拖动 + 缩放一个方框,点「识别」只对框内做 OCR+LLM。
/// 可反复挪框重识别,满意后进入核对页;0 项也能进核对手动补(失败回退红线)。
struct RegionAdjustView: View {
    let image: UIImage
    /// 对裁好的子图跑 OCR+LLM,返回(识别项, 提示文案?)。几何裁剪由本视图负责。
    let recognize: (UIImage) async -> (items: [QuickRegionItem], warning: String?)
    let onProceed: ([QuickRegionItem]) -> Void
    let onRetake: () -> Void
    let onCancel: () -> Void

    /// 单次识别超时(秒)。超时取消并提示挪框重试。
    let timeoutSeconds: Int = 60

    @State private var box: CGRect = .zero
    @State private var fittedRect: CGRect = .zero
    @State private var boxInited = false
    @State private var dragStartBox: CGRect? = nil
    @State private var resizeStartBox: CGRect? = nil

    @State private var isRecognizing = false
    @State private var items: [QuickRegionItem] = []
    @State private var statusText: String? = nil
    @State private var recognizeTask: Task<Void, Never>? = nil

    private let handleSize: CGFloat = 30
    private let minBox: CGFloat = 56

    var body: some View {
        VStack(spacing: 0) {
            topBar
            canvas
            controls
        }
        .background(Color.black.ignoresSafeArea())
    }

    // MARK: - 顶栏

    private var topBar: some View {
        HStack {
            Button {
                recognizeTask?.cancel()
                onCancel()
            } label: {
                Text("取消")
                    .font(.tjScaled( 16, weight: .medium))
                    .foregroundStyle(.white)
                    .padding(.horizontal, 12)
                    .frame(minWidth: 60, minHeight: 44)   // HIG 最小命中区,命中整块而非文字
                    .contentShape(Rectangle())
            }
            .buttonStyle(.plain)
            Spacer()
            Text("框住异常指标")
                .font(.tjScaled( 16, weight: .semibold))
                .foregroundStyle(.white)
            Spacer()
            Button {
                recognizeTask?.cancel()
                onRetake()
            } label: {
                Text("重拍")
                    .font(.tjScaled( 16, weight: .medium))
                    .foregroundStyle(.white)
                    .padding(.horizontal, 12)
                    .frame(minWidth: 60, minHeight: 44)
                    .contentShape(Rectangle())
            }
            .buttonStyle(.plain)
        }
        .padding(.horizontal, 8)
        .padding(.vertical, 4)
        .background(Color.black)
    }

    // MARK: - 图 + 选框

    private var canvas: some View {
        GeometryReader { proxy in
            let fitted = AVMakeRect(
                aspectRatio: image.size == .zero ? CGSize(width: 1, height: 1) : image.size,
                insideRect: CGRect(origin: .zero, size: proxy.size)
            )
            ZStack {
                Color.black

                Image(uiImage: image)
                    .resizable()
                    .scaledToFit()
                    .frame(width: proxy.size.width, height: proxy.size.height)

                // 框外压暗,突出框内
                Canvas { ctx, size in
                    var path = Path(CGRect(origin: .zero, size: size))
                    path.addPath(Path(roundedRect: box, cornerRadius: 10))
                    ctx.fill(path, with: .color(.black.opacity(0.5)), style: FillStyle(eoFill: true))
                }
                .allowsHitTesting(false)

                // 选框边框 + 拖动
                RoundedRectangle(cornerRadius: 10, style: .continuous)
                    .strokeBorder(Color.white, style: StrokeStyle(lineWidth: 2, dash: [7, 5]))
                    .frame(width: max(box.width, 1), height: max(box.height, 1))
                    .position(x: box.midX, y: box.midY)
                    .contentShape(Rectangle())
                    .gesture(moveGesture(in: fitted))

                // 右下角缩放手柄
                Circle()
                    .fill(.white)
                    .frame(width: handleSize, height: handleSize)
                    .overlay(
                        Image(systemName: "arrow.down.right")
                            .font(.system(size: 12, weight: .bold))
                            .foregroundStyle(.black)
                    )
                    .position(x: box.maxX, y: box.maxY)
                    .gesture(resizeGesture(in: fitted))
            }
            .onAppear {
                fittedRect = fitted
                if !boxInited {
                    box = defaultBox(in: fitted)
                    boxInited = true
                }
            }
            .onChange(of: proxy.size) { _, _ in
                fittedRect = fitted
                box = clampSize(clampOrigin(box, in: fitted), in: fitted)
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.black)
    }

    // MARK: - 底部控件

    private var controls: some View {
        VStack(spacing: 12) {
            Text(statusText ?? String(appLoc: "拖动方框对准要识别的指标,可拖右下角缩放"))
                .font(.tjScaled( 13))
                .foregroundStyle(.white.opacity(0.85))
                .multilineTextAlignment(.center)
                .frame(maxWidth: .infinity)
                .frame(minHeight: 34)

            Button {
                runRecognize()
            } label: {
                HStack(spacing: 8) {
                    if isRecognizing { ProgressView().tint(.black) }
                    Text(isRecognizing ? String(appLoc: "本地识别中…") : String(appLoc: "识别框内指标"))
                        .font(.tjScaled( 16, weight: .semibold))
                }
                .frame(maxWidth: .infinity)
                .padding(.vertical, 14)
                .background(Capsule().fill(.white))
                .foregroundStyle(.black)
            }
            .disabled(isRecognizing)

            Button {
                recognizeTask?.cancel()
                onProceed(items)
            } label: {
                Text(items.isEmpty
                     ? String(appLoc: "跳过 · 手动录入")
                     : String(appLoc: "进入核对(\(items.count))"))
                    .font(.tjScaled( 15, weight: .medium))
                    .foregroundStyle(.white)
                    .frame(maxWidth: .infinity)
                    .padding(.vertical, 10)
                    .background(Capsule().strokeBorder(.white.opacity(0.6), lineWidth: 1))
            }
            .disabled(isRecognizing)
        }
        .padding(.horizontal, 20)
        .padding(.top, 14)
        .padding(.bottom, 28)
        .background(Color.black)
    }

    // MARK: - 选框默认值 / 夹紧

    private func defaultBox(in fitted: CGRect) -> CGRect {
        guard fitted.width > 0, fitted.height > 0 else { return .zero }
        let w = fitted.width * 0.8
        let h = min(fitted.height * 0.3, max(minBox, fitted.height * 0.18))
        let x = fitted.minX + (fitted.width - w) / 2
        let y = fitted.minY + (fitted.height - h) / 2
        return CGRect(x: x, y: y, width: w, height: h)
    }

    /// 移动时夹紧原点(尺寸不变),保证框不超出图片显示区。
    private func clampOrigin(_ b: CGRect, in fitted: CGRect) -> CGRect {
        guard fitted.width > 0 else { return b }
        let w = min(b.width, fitted.width)
        let h = min(b.height, fitted.height)
        let x = min(max(b.minX, fitted.minX), fitted.maxX - w)
        let y = min(max(b.minY, fitted.minY), fitted.maxY - h)
        return CGRect(x: x, y: y, width: w, height: h)
    }

    /// 缩放时夹紧尺寸,保证不超出图片显示区。
    private func clampSize(_ b: CGRect, in fitted: CGRect) -> CGRect {
        guard fitted.width > 0 else { return b }
        let w = max(minBox, min(b.width, fitted.maxX - b.minX))
        let h = max(minBox, min(b.height, fitted.maxY - b.minY))
        return CGRect(x: b.minX, y: b.minY, width: w, height: h)
    }

    private func moveGesture(in fitted: CGRect) -> some Gesture {
        DragGesture()
            .onChanged { v in
                if dragStartBox == nil { dragStartBox = box }
                let start = dragStartBox ?? box
                let moved = start.offsetBy(dx: v.translation.width, dy: v.translation.height)
                box = clampOrigin(moved, in: fitted)
            }
            .onEnded { _ in dragStartBox = nil }
    }

    private func resizeGesture(in fitted: CGRect) -> some Gesture {
        DragGesture()
            .onChanged { v in
                if resizeStartBox == nil { resizeStartBox = box }
                let start = resizeStartBox ?? box
                let grown = CGRect(
                    x: start.minX, y: start.minY,
                    width: start.width + v.translation.width,
                    height: start.height + v.translation.height
                )
                box = clampSize(grown, in: fitted)
            }
            .onEnded { _ in resizeStartBox = nil }
    }

    // MARK: - 识别

    private func runRecognize() {
        guard !isRecognizing, fittedRect.width > 1, box.width > 1, box.height > 1 else { return }
        let cropped = RegionImageCropper.cropAspectFit(image, box: box, imageFrame: fittedRect)
        recognizeTask?.cancel()
        isRecognizing = true
        statusText = String(appLoc: "本地识别中…")
        recognizeTask = Task {
            let watchdog = Task {
                try? await Task.sleep(for: .seconds(timeoutSeconds))
                recognizeTask?.cancel()
            }
            defer { watchdog.cancel() }

            let result = await recognize(cropped)
            isRecognizing = false
            if Task.isCancelled {
                statusText = String(appLoc: "识别超时,挪一下框再试或手动补充")
                return
            }
            items = result.items
            statusText = result.warning
                ?? String(appLoc: "识别到 \(result.items.count) 项,可继续挪框或进入核对")
        }
    }
}
