import SwiftUI
import Network
import UniformTypeIdentifiers

/// 「我的 · 模型管理」页:分模型卡片显示下载状态/进度,支持下载全部/暂停 + 旁路文件导入。
/// 只观察 ModelDownloadService 的状态,不直接碰 URLSession(§3.1)。
struct ModelManagementView: View {
    @State private var service = ModelDownloadService.shared
    @State private var isCellular = false
    @State private var showCellularConfirm = false
    @State private var showImporter = false
    @State private var importError: String?

    private let monitor = NWPathMonitor()
    private let monitorQueue = DispatchQueue(label: "kk.netmonitor")

    private var allReady: Bool {
        ModelKind.userFacing.allSatisfy { service.states[$0]?.phase == .ready }
    }

    var body: some View {
        ScrollView {
            VStack(spacing: 14) {
                ForEach(ModelKind.userFacing, id: \.self) { kind in
                    modelCard(kind)
                }

                actionButtons
                    .padding(.top, 4)

                if let importError {
                    Text(importError)
                        .font(.tjScaled( 12))
                        .foregroundStyle(Tj.Palette.brick)
                        .frame(maxWidth: .infinity, alignment: .leading)
                }

                footer
                    .padding(.top, 8)
            }
            .padding(.horizontal, 16)
            .padding(.vertical, 18)
        }
        .background(Tj.Palette.sand.ignoresSafeArea())
        .navigationTitle("模型管理")
        .navigationBarTitleDisplayMode(.inline)
        .onAppear {
            service.refreshStates()
            monitor.pathUpdateHandler = { path in
                let cellular = path.status == .satisfied && path.usesInterfaceType(.cellular)
                Task { @MainActor in isCellular = cellular }
            }
            monitor.start(queue: monitorQueue)
        }
        .onDisappear { monitor.cancel() }
        .fileImporter(isPresented: $showImporter,
                      allowedContentTypes: [.folder]) { handleImport($0) }
        .alert("使用蜂窝网络下载?", isPresented: $showCellularConfirm) {
            Button("取消", role: .cancel) {}
            Button("继续下载") { service.downloadAll() }
        } message: {
            Text("模型约 \(formatBytes(totalAllBytes)),建议在 Wi-Fi 下下载。")
        }
    }

    // MARK: - 模型卡片

    private func modelCard(_ kind: ModelKind) -> some View {
        let state = service.states[kind]
            ?? DownloadState(phase: .idle, receivedBytes: 0,
                             totalBytes: ModelManifest.totalBytes(for: kind), bytesPerSecond: 0)
        return VStack(alignment: .leading, spacing: 10) {
            HStack(alignment: .top) {
                VStack(alignment: .leading, spacing: 3) {
                    Text(kind.displayName)
                        .font(.tjScaled( 15, weight: .semibold))
                        .foregroundStyle(Tj.Palette.text)
                    Text(subtitle(kind))
                        .font(.tjScaled( 12))
                        .foregroundStyle(Tj.Palette.text3)
                }
                Spacer()
                statusBadge(state.phase)
            }

            if state.phase == .downloading {
                ProgressView(value: min(max(state.fraction, 0), 1))
                    .tint(Tj.Palette.ink)
                HStack {
                    Text("\(Int(state.fraction * 100))%")
                    Spacer()
                    Text(speedText(state))
                }
                .font(.tjScaled( 11, design: .monospaced))
                .foregroundStyle(Tj.Palette.text3)
            } else {
                HStack {
                    Text(formatBytes(ModelManifest.totalBytes(for: kind)))
                        .font(.tjScaled( 11, design: .monospaced))
                        .foregroundStyle(Tj.Palette.text3)
                    Spacer()
                    if case .failed(let message) = state.phase {
                        Text(message)
                            .font(.tjScaled( 11))
                            .foregroundStyle(Tj.Palette.brick)
                            .lineLimit(1)
                    }
                }
            }
        }
        .padding(14)
        .frame(maxWidth: .infinity, alignment: .leading)
        .tjCard()
        .contentShape(Rectangle())
        .onTapGesture {
            if case .failed = state.phase { service.download(kind) }
        }
    }

    private func statusBadge(_ phase: DownloadPhase) -> some View {
        switch phase {
        case .idle:        return TjBadge(text: String(appLoc: "待下载"), style: .neutral)
        case .downloading: return TjBadge(text: String(appLoc: "下载中"), style: .amber)
        case .verifying:   return TjBadge(text: String(appLoc: "校验中"), style: .amber)
        case .ready:       return TjBadge(text: String(appLoc: "已就绪"), style: .leaf)
        case .failed:      return TjBadge(text: String(appLoc: "失败 · 重试"), style: .brick)
        }
    }

    // MARK: - 动作按钮

    @ViewBuilder
    private var actionButtons: some View {
        if service.isAnyDownloading {
            Button {
                for kind in ModelKind.userFacing { service.cancel(kind) }
            } label: {
                Text("暂停下载").frame(maxWidth: .infinity)
            }
            .buttonStyle(TjGhostButton())
        } else if allReady {
            HStack(spacing: 6) {
                Image(systemName: "checkmark.seal.fill")
                Text("Qwen3.5-2B 已就绪")
            }
            .font(.tjScaled( 13, weight: .semibold))
            .foregroundStyle(Tj.Palette.leaf)
            .frame(maxWidth: .infinity)
            .padding(.vertical, 6)
        } else {
            Button {
                if isCellular { showCellularConfirm = true } else { service.downloadAll() }
            } label: {
                Text("下载全部模型 · \(formatBytes(totalAllBytes))")
                    .frame(maxWidth: .infinity)
            }
            .buttonStyle(TjPrimaryButton())
        }

        Button {
            importError = nil
            showImporter = true
        } label: {
            Text("从文件导入(离线)").frame(maxWidth: .infinity)
        }
        .buttonStyle(TjGhostButton())
    }

    private var footer: some View {
        VStack(spacing: 8) {
            TjLockChip()
            Text("100% 本地推理 · 模型仅需下载一次")
                .font(.tjScaled( 11))
                .foregroundStyle(Tj.Palette.text3)
        }
        .frame(maxWidth: .infinity)
    }

    // MARK: - 旁路导入

    private func handleImport(_ result: Result<URL, Error>) {
        do {
            let folder = try result.get()
            let scoped = folder.startAccessingSecurityScopedResource()
            defer { if scoped { folder.stopAccessingSecurityScopedResource() } }

            let name = folder.lastPathComponent
            guard let kind = ModelKind.userFacing.first(where: { $0.rawValue == name }) else {
                let names = ModelKind.userFacing.map(\.rawValue).joined(separator: " 或 ")
                importError = String(appLoc: "请选择名为 \(names) 的文件夹")
                return
            }
            try service.importModel(kind, from: folder)
            importError = nil
        } catch {
            importError = String(appLoc: "导入失败:\(error.localizedDescription)")
        }
    }

    // MARK: - 辅助

    private var totalAllBytes: Int {
        ModelKind.userFacing.reduce(0) { $0 + ModelManifest.totalBytes(for: $1) }
    }

    private func subtitle(_ kind: ModelKind) -> String {
        switch kind {
        case .llm:    return String(appLoc: "文本解读 · 趋势 / 问答(MLX 兜底)")
        case .vl:     return String(appLoc: "拍照识别报告 → 结构化指标")
        case .mnnLLM: return String(appLoc: "文本解读 + 拍照识别 · MNN + SME2 端侧加速")
        }
    }

    private func formatBytes(_ bytes: Int) -> String {
        ByteCountFormatter.string(fromByteCount: Int64(bytes), countStyle: .file)
    }

    private func speedText(_ state: DownloadState) -> String {
        guard state.bytesPerSecond > 0 else { return "—" }
        return formatBytes(Int(state.bytesPerSecond)) + "/s"
    }
}

#Preview {
    NavigationStack {
        ModelManagementView()
    }
}
