import SwiftUI
import SwiftData
import UIKit
import Combine

/// 异常项快拍 · 统一流程。
/// 局部小框拍摄 → VL 识别(只抽 indicators)→ 确认 → 存成独立 Indicator(不建 Report、不留图)。
///
/// 状态机:
/// ```
/// idle(相机/相册) → analyzing(croppedImage) → confirm(items)
///                         ↓ 失败/超时
///                     confirm(空 + warning)
/// confirm → save → dismiss   ·   confirm → 重拍 → idle
/// ```
struct QuickRegionCaptureFlow: View {
    @Environment(\.modelContext) private var ctx
    let onClose: () -> Void

    @State private var phase: Phase = .idle
    @State private var analyzeTask: Task<Void, Never>? = nil

    /// VL 单次推理超时(防卡死);超时后 cancel 子任务,UI 转手动录入。
    private let analyzeTimeoutSeconds: Int = 30

    enum Phase {
        case idle
        case analyzing(image: UIImage)
        case confirm(image: UIImage?, items: [QuickRegionItem], warning: String?)
    }

    var body: some View {
        content
            .background(Tj.Palette.sand.ignoresSafeArea())
    }

    @ViewBuilder
    private var content: some View {
        switch phase {
        case .idle:
            captureEntry
                .ignoresSafeArea()

        case .analyzing(let image):
            NavigationStack {
                AnalyzingRegionView(
                    image: image,
                    timeoutSeconds: analyzeTimeoutSeconds,
                    onCancel: {
                        analyzeTask?.cancel()
                        analyzeTask = nil
                        // 取消识别 → 直接进确认页手动补充(图仍在内存,可重拍)
                        phase = .confirm(image: image, items: [],
                                         warning: String(appLoc: "已取消识别,手动补充或重拍"))
                    }
                )
                .navigationTitle(String(appLoc: "本地识别中…"))
                .navigationBarTitleDisplayMode(.inline)
                .toolbar {
                    ToolbarItem(placement: .topBarLeading) {
                        Button("取消") { cancelAll() }
                            .foregroundStyle(Tj.Palette.text)
                    }
                }
            }

        case .confirm(let image, let items, let warning):
            NavigationStack {
                QuickRegionConfirmView(
                    image: image,
                    items: items,
                    warning: warning,
                    onSave: { finalItems, capturedAt in save(items: finalItems, capturedAt: capturedAt) },
                    onCancel: cancelAll,
                    onRetake: { phase = .idle }
                )
                .navigationTitle(String(appLoc: "核对异常项"))
                .navigationBarTitleDisplayMode(.inline)
                .toolbar {
                    ToolbarItem(placement: .topBarLeading) {
                        Button("取消") { cancelAll() }
                            .foregroundStyle(Tj.Palette.text)
                    }
                }
            }
        }
    }

    // MARK: - 入口:相机(真机)/ 相册(模拟器)

    @ViewBuilder
    private var captureEntry: some View {
        #if targetEnvironment(simulator)
        PhotoPickerSheet(
            onFinish: { imgs in if let first = imgs.first { startAnalyze(image: first) } },
            onCancel: onClose
        )
        #else
        RegionCameraView(
            onCapture: { startAnalyze(image: $0) },
            onCancel: onClose
        )
        #endif
    }

    // MARK: - 识别

    private func startAnalyze(image: UIImage) {
        analyzeTask?.cancel()
        phase = .analyzing(image: image)
        let timeout = analyzeTimeoutSeconds
        // 本类型默认 MainActor 隔离,Task{} 继承之,故内部 phase 写入都在主线程,直接赋值即可。
        analyzeTask = Task {
            guard let data = image.jpegData(compressionQuality: 0.9) else {
                phase = .confirm(image: image, items: [],
                              warning: String(appLoc: "图片编码失败,手动补充或重拍"))
                return
            }

            let watchdog = Task {
                try? await Task.sleep(for: .seconds(timeout))
                analyzeTask?.cancel()
            }
            defer { watchdog.cancel() }

            do {
                let parsed = try await CaptureService.shared.recognizeRegion(imageData: data)
                if Task.isCancelled {
                    phase = .confirm(image: image, items: [],
                                  warning: String(appLoc: "识别超时(>\(timeout)s),手动补充或重拍"))
                    return
                }
                let items = Self.buildItems(from: parsed)
                phase = .confirm(
                    image: image,
                    items: items,
                    warning: items.isEmpty ? String(appLoc: "没读出指标,手动补充或重拍") : nil
                )
            } catch CaptureError.modelNotReady {
                phase = .confirm(image: image, items: [],
                              warning: String(appLoc: "VL 模型未就绪,手动补充"))
            } catch let CaptureError.parseFailed(msg) {
                phase = .confirm(image: image, items: [],
                              warning: String(appLoc: "VL 输出无法解析:\(msg)"))
            } catch let CaptureError.inferenceFailed(msg) {
                phase = .confirm(image: image, items: [],
                              warning: Task.isCancelled
                                  ? String(appLoc: "识别超时(>\(timeout)s),手动补充或重拍")
                                  : String(appLoc: "推理失败:\(msg)"))
            } catch {
                phase = .confirm(image: image, items: [],
                              warning: String(appLoc: "未知错误:\(error.localizedDescription)"))
            }
        }
    }

    /// VL 结果 → 可编辑行,异常项(high/low)置顶、默认勾选。
    private static func buildItems(from parsed: [ParsedReport.ParsedIndicator]) -> [QuickRegionItem] {
        let mapped = parsed.map {
            QuickRegionItem(name: $0.name, value: $0.value, unit: $0.unit,
                            range: $0.range, status: $0.status, include: true)
        }
        // 异常优先(stable):high/low 在前,normal 在后
        return mapped.enumerated().sorted { a, b in
            let aAbn = a.element.status != .normal
            let bAbn = b.element.status != .normal
            if aAbn != bAbn { return aAbn && !bAbn }
            return a.offset < b.offset
        }.map { $0.element }
    }

    // MARK: - 取消 / 保存

    private func cancelAll() {
        analyzeTask?.cancel()
        analyzeTask = nil
        onClose()
    }

    /// 勾选项各存一条独立 Indicator(与「记录指标」自由输入一致):无 Report、无 Asset、无 seriesKey。
    private func save(items: [QuickRegionItem], capturedAt: Date) {
        let selected = items.filter {
            $0.include
            && !$0.name.trimmingCharacters(in: .whitespaces).isEmpty
            && !$0.value.trimmingCharacters(in: .whitespaces).isEmpty
        }
        for item in selected {
            let indicator = Indicator(
                name: item.name.trimmingCharacters(in: .whitespaces),
                value: item.value.trimmingCharacters(in: .whitespaces),
                unit: item.unit.trimmingCharacters(in: .whitespaces),
                range: item.range.trimmingCharacters(in: .whitespaces),
                status: item.status,
                capturedAt: capturedAt
            )
            ctx.insert(indicator)
        }
        try? ctx.save()
        onClose()
    }
}

// MARK: - 识别中视图

private struct AnalyzingRegionView: View {
    let image: UIImage
    let timeoutSeconds: Int
    let onCancel: () -> Void

    @State private var elapsed: Int = 0
    private let tick = Timer.publish(every: 1, on: .main, in: .common).autoconnect()

    var body: some View {
        VStack(spacing: 20) {
            Spacer()
            Image(uiImage: image)
                .resizable()
                .scaledToFit()
                .frame(maxHeight: 200)
                .clipShape(RoundedRectangle(cornerRadius: Tj.Radius.md, style: .continuous))
                .overlay(
                    RoundedRectangle(cornerRadius: Tj.Radius.md, style: .continuous)
                        .strokeBorder(Tj.Palette.line, lineWidth: 1)
                )
                .overlay(
                    RoundedRectangle(cornerRadius: Tj.Radius.md, style: .continuous)
                        .fill(.ultraThinMaterial)
                        .overlay(ProgressView().tint(Tj.Palette.ink).scaleEffect(1.3))
                )
            VStack(spacing: 6) {
                Text("识别框内指标")
                    .font(.tjH2())
                    .foregroundStyle(Tj.Palette.text)
                Text("100% 本地推理 · 已用 \(elapsed)s")
                    .font(.system(size: 12))
                    .foregroundStyle(Tj.Palette.text3)
                if elapsed >= timeoutSeconds - 5 {
                    Text("快超时了,>\(timeoutSeconds)s 会自动转手动录入")
                        .font(.system(size: 11))
                        .foregroundStyle(Tj.Palette.amber)
                }
            }
            Button("取消识别 · 改为手动录入", action: onCancel)
                .font(.system(size: 13, weight: .medium))
                .foregroundStyle(Tj.Palette.text3)
                .padding(.top, 4)
            Spacer()
        }
        .padding(.horizontal, 20)
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Tj.Palette.sand)
        .onReceive(tick) { _ in elapsed += 1 }
    }
}
