import SwiftUI
import SwiftData

/// 一个指标的「同类组」标识。决定聚合详情里收哪些历次记录、跳哪个趋势 bucket。
/// - `.series`:有 seriesKey 的长期监测指标(血糖/体重/...)。
/// - `.bloodPressure`:血压(bp.systolic + bp.diastolic 合并成一对)。
/// - `.lab`:无 seriesKey 的化验/手动指标,按 name+unit 归一化 key 聚合。
enum IndicatorGroup: Identifiable, Hashable {
    case series(key: String)
    case bloodPressure
    case lab(key: String)

    var id: String {
        switch self {
        case .series(let k): return "series:\(k)"
        case .bloodPressure: return "bp"
        case .lab(let k):    return "lab:\(k)"
        }
    }

    /// 从单条指标推断其所属同类组(与趋势页 SeriesBucket 的分组语义一致)。
    static func of(_ i: Indicator) -> IndicatorGroup {
        if let key = i.seriesKey, !key.isEmpty {
            return key.hasPrefix("bp.") ? .bloodPressure : .series(key: key)
        }
        return .lab(key: SeriesBucket.normalizedKey(name: i.name, unit: i.unit))
    }
}

/// 同类指标聚合详情:横向翻页看该指标的历次记录,底部可跳趋势图。
/// 从「记录」页点指标条目进入。数据全部 @Query 实时,删除后翻页列表自动更新。
struct IndicatorSeriesDetailView: View {
    @Environment(\.dismiss) private var dismiss
    @Environment(\.modelContext) private var ctx

    let group: IndicatorGroup

    @Query(sort: \Indicator.capturedAt, order: .reverse)
    private var indicators: [Indicator]
    @Query private var profiles: [UserProfile]
    @Query private var customMetrics: [CustomMonitorMetric]

    @State private var selection: String?
    @State private var showTrend = false
    @State private var showDeleteConfirm = false
    @State private var evidenceTarget: Indicator?

    // MARK: - 数据

    /// 聚合详情里的一页:单值指标一条;血压一对。
    private enum Record: Identifiable {
        case single(Indicator)
        case bp(sys: Indicator, dia: Indicator?)

        var id: String {
            switch self {
            case .single(let i): return "\(i.persistentModelID)"
            case .bp(let s, _):  return "bp-\(s.persistentModelID)"
            }
        }
    }

    /// 历次血压对:以 bp.systolic 为锚,按 ±5s 配 bp.diastolic(同 TimelineEntry 合并规则)。
    private var bloodPressureRecords: [Record] {
        // 收缩压、舒张压各过滤一次(O(n)),再在小得多的舒张压子集里配对,
        // 避免「每条收缩压都重扫整张指标表」的 O(n²) 放大。
        let sysList = indicators
            .filter { $0.seriesKey == "bp.systolic" }
            .sorted { $0.capturedAt > $1.capturedAt }
        let diaList = indicators.filter { $0.seriesKey == "bp.diastolic" }
        var usedDia = Set<PersistentIdentifier>()
        return sysList.map { sys in
            let dia = diaList.first {
                !usedDia.contains($0.persistentModelID) &&
                abs($0.capturedAt.timeIntervalSince(sys.capturedAt)) <= 5
            }
            if let dia { usedDia.insert(dia.persistentModelID) }
            return .bp(sys: sys, dia: dia)
        }
    }

    private var records: [Record] {
        switch group {
        case .bloodPressure:
            return bloodPressureRecords
        case .series(let key):
            return indicators
                .filter { $0.seriesKey == key }
                .sorted { $0.capturedAt > $1.capturedAt }
                .map(Record.single)
        case .lab(let nk):
            return indicators
                .filter {
                    ($0.seriesKey ?? "").isEmpty &&
                    SeriesBucket.normalizedKey(name: $0.name, unit: $0.unit) == nk
                }
                .sorted { $0.capturedAt > $1.capturedAt }
                .map(Record.single)
        }
    }

    private var title: String {
        switch group {
        case .bloodPressure:
            return String(appLoc: "血压")
        case .series, .lab:
            if case let .single(i)? = records.first { return i.name }
            return String(appLoc: "指标详情")
        }
    }

    /// 对应的趋势 bucket(需 ≥2 个可解析数值点才存在);nil 时隐藏「查看趋势图」。
    private var bucket: SeriesBucket? {
        let all = SeriesBucket.build(from: indicators,
                                     profile: profiles.first,
                                     customMetrics: customMetrics)
        switch group {
        case .bloodPressure:
            return all.first { $0.id == "bp" }
        case .series(let key):
            return all.first { b in b.lines.contains { $0.seriesKey == key } }
        case .lab(let nk):
            return all.first { $0.kind == .lab && $0.id == "lab:\(nk)" }
        }
    }

    private var currentIndex: Int {
        records.firstIndex { $0.id == selection } ?? 0
    }

    // MARK: - Body

    var body: some View {
        NavigationStack {
            VStack(spacing: 0) {
                header
                if records.isEmpty {
                    Spacer()
                    TjPlaceholder(label: String(appLoc: "记录已不存在"))
                        .frame(width: 200, height: 120)
                    Spacer()
                } else {
                    pages
                    pager
                    recordAnotherRow
                    if bucket != nil { trendButton }
                }
            }
            .background(Tj.Palette.sand.ignoresSafeArea())
            .navigationDestination(isPresented: $showTrend) {
                if let bucket { TrendDetailView(bucket: bucket) }
            }
        }
        .presentationDetents([.medium, .large])
        .presentationDragIndicator(.visible)
        .presentationBackground(Tj.Palette.sand)
        .presentationCornerRadius(Tj.Radius.xl)
        .onAppear { if selection == nil { selection = records.first?.id } }
        .alert(String(appLoc: "永久删除这条记录?"), isPresented: $showDeleteConfirm) {
            Button(String(appLoc: "删除"), role: .destructive) { deleteCurrent() }
            Button(String(appLoc: "取消"), role: .cancel) { }
        } message: {
            Text("删除后无法恢复。")
        }
        .sheet(item: $evidenceTarget) { indicator in
            if let report = indicator.report {
                EvidenceImagePreview(report: report, indicator: indicator)
            }
        }
    }

    // MARK: - Header

    private var header: some View {
        HStack(spacing: 12) {
            Button { dismiss() } label: {
                Image(systemName: "xmark")
                    .font(.tjScaled(16, weight: .semibold))
                    .foregroundStyle(Tj.Palette.text)
                    .frame(width: 32, height: 32)
                    .background(Circle().fill(Tj.Palette.sand2))
            }
            Text(title)
                .font(.tjH2())
                .foregroundStyle(Tj.Palette.text)
                .lineLimit(1)
            if records.count > 1 {
                Text("\(records.count) 条")
                    .font(.tjScaled(12))
                    .foregroundStyle(Tj.Palette.text3)
            }
            Spacer()
            TjLockChip()
        }
        .padding(.horizontal, 20)
        .padding(.vertical, 14)
        .background(Tj.Palette.sand)
        .overlay(alignment: .bottom) {
            Rectangle().fill(Tj.Palette.lineSoft).frame(height: 1)
        }
    }

    // MARK: - 翻页内容

    private var pages: some View {
        TabView(selection: $selection) {
            ForEach(records) { rec in
                ScrollView {
                    VStack(alignment: .leading, spacing: 16) {
                        recordCard(rec)
                        deleteButton
                    }
                    .padding(.horizontal, 20)
                    .padding(.vertical, 16)
                    .frame(maxWidth: .infinity, alignment: .leading)
                }
                .tag(Optional(rec.id))
            }
        }
        .tabViewStyle(.page(indexDisplayMode: .never))
    }

    @ViewBuilder
    private func recordCard(_ rec: Record) -> some View {
        switch rec {
        case .single(let i):            singleCard(i)
        case .bp(let sys, let dia):     bpCard(sys: sys, dia: dia)
        }
    }

    private func singleCard(_ i: Indicator) -> some View {
        card {
            HStack(alignment: .firstTextBaseline) {
                Text(i.name).font(.tjH2()).foregroundStyle(Tj.Palette.text)
                Spacer()
                statusChip(i.status)
            }
            HStack(alignment: .firstTextBaseline, spacing: 4) {
                Text(i.value)
                    .font(.tjScaled(30, weight: .bold, design: .rounded))
                    .foregroundStyle(i.status == .normal ? Tj.Palette.text : Tj.Palette.brick)
                if !i.unit.isEmpty {
                    Text(i.unit).font(.tjScaled(14)).foregroundStyle(Tj.Palette.text3)
                }
            }
            divider
            if !i.range.isEmpty { field(String(appLoc: "参考范围"), i.range) }
            field(String(appLoc: "记录时间"), Self.dateTimeText(i.capturedAt))
            field(String(appLoc: "来源"), i.report?.title ?? i.source.label)
            if i.report != nil { evidenceButton(for: i) }
            if let note = i.note, !note.isEmpty { field(String(appLoc: "备注"), note) }
        }
    }

    private func bpCard(sys: Indicator, dia: Indicator?) -> some View {
        let combined: IndicatorStatus = sys.status != .normal ? sys.status : (dia?.status ?? .normal)
        return card {
            HStack(alignment: .firstTextBaseline) {
                Text(String(appLoc: "血压")).font(.tjH2()).foregroundStyle(Tj.Palette.text)
                Spacer()
                statusChip(combined)
            }
            HStack(alignment: .firstTextBaseline, spacing: 4) {
                Text("\(sys.value)/\(dia?.value ?? "—")")
                    .font(.tjScaled(30, weight: .bold, design: .rounded))
                    .foregroundStyle(combined == .normal ? Tj.Palette.text : Tj.Palette.brick)
                Text("mmHg").font(.tjScaled(14)).foregroundStyle(Tj.Palette.text3)
            }
            divider
            if !sys.range.isEmpty { field(String(appLoc: "参考范围"), sys.range) }
            field(String(appLoc: "记录时间"), Self.dateTimeText(sys.capturedAt))
        }
    }

    // MARK: - 翻页器

    private var pager: some View {
        VStack(spacing: 8) {
            HStack(spacing: 20) {
                pagerArrow("chevron.left", enabled: currentIndex > 0) {
                    if currentIndex > 0 { selection = records[currentIndex - 1].id }
                }
                if records.count <= 7 {
                    HStack(spacing: 6) {
                        ForEach(Array(records.enumerated()), id: \.offset) { idx, _ in
                            Circle()
                                .fill(idx == currentIndex ? Tj.Palette.ink : Tj.Palette.line)
                                .frame(width: 6, height: 6)
                        }
                    }
                }
                pagerArrow("chevron.right", enabled: currentIndex < records.count - 1) {
                    if currentIndex < records.count - 1 { selection = records[currentIndex + 1].id }
                }
            }
            Text("第 \(currentIndex + 1) / 共 \(records.count) 条")
                .font(.tjScaled(11, design: .monospaced))
                .foregroundStyle(Tj.Palette.text3)
        }
        .padding(.top, 4)
        .padding(.bottom, 10)
        .frame(maxWidth: .infinity)
    }

    private func pagerArrow(_ system: String, enabled: Bool, action: @escaping () -> Void) -> some View {
        Button(action: action) {
            Image(systemName: system)
                .font(.tjScaled(13, weight: .semibold))
                .foregroundStyle(enabled ? Tj.Palette.text : Tj.Palette.text3.opacity(0.4))
                .frame(width: 30, height: 30)
                .background(Circle().fill(Tj.Palette.sand2))
        }
        .buttonStyle(.plain)
        .disabled(!enabled)
    }

    // MARK: - 再记一条(与指标详情共用 RecordAnotherButton 组件)

    /// 按当前翻到的那一页指标预选「再记一条」:血压走双字段,其余按 name/unit/range/seriesKey。
    @ViewBuilder
    private var recordAnotherRow: some View {
        if records.indices.contains(currentIndex) {
            switch records[currentIndex] {
            case .single(let i):
                RecordAnotherButton(name: i.name, prefill: .init(indicator: i))
                    .padding(.horizontal, 20)
                    .padding(.bottom, bucket == nil ? 20 : 10)
            case .bp(let sys, _):
                RecordAnotherButton(
                    name: String(appLoc: "血压"),
                    prefill: .init(seriesKey: sys.seriesKey ?? "bp.systolic",
                                   name: String(appLoc: "血压"),
                                   unit: "mmHg", range: sys.range)
                )
                .padding(.horizontal, 20)
                .padding(.bottom, bucket == nil ? 20 : 10)
            }
        }
    }

    // MARK: - 趋势 / 删除

    private var trendButton: some View {
        Button { showTrend = true } label: {
            Label(String(appLoc: "查看趋势图"), systemImage: "chart.xyaxis.line")
                .font(.tjScaled(15, weight: .semibold))
                .foregroundStyle(Tj.Palette.paper)
                .frame(maxWidth: .infinity)
                .padding(.vertical, 14)
                .background(
                    RoundedRectangle(cornerRadius: Tj.Radius.md, style: .continuous)
                        .fill(Tj.Palette.ink)
                )
        }
        .buttonStyle(.plain)
        .padding(.horizontal, 20)
        .padding(.bottom, 20)
    }

    private var deleteButton: some View {
        Button(role: .destructive) { showDeleteConfirm = true } label: {
            Label(String(appLoc: "永久删除"), systemImage: "trash")
                .font(.tjScaled(12, weight: .medium))
                .foregroundStyle(Tj.Palette.brick.opacity(0.8))
                .padding(.horizontal, 14)
                .padding(.vertical, 8)
                .background(
                    RoundedRectangle(cornerRadius: Tj.Radius.md, style: .continuous)
                        .strokeBorder(Tj.Palette.brick.opacity(0.3), lineWidth: 1)
                )
                .contentShape(Rectangle())
        }
        .buttonStyle(.plain)
        .padding(.top, 8)
    }

    /// 删当前页记录(永久:SwiftData 硬删 + Vault 原图 unlink,见 CLAUDE.md §6)。
    /// 删后把 selection 落到相邻一条;删空则关闭。
    private func deleteCurrent() {
        guard records.indices.contains(currentIndex) else { return }
        let removingIndex = currentIndex
        switch records[removingIndex] {
        case .single(let i):
            deleteIndicator(i)
        case .bp(let sys, let dia):
            deleteIndicator(sys)
            if let dia { deleteIndicator(dia) }
        }
        try? ctx.save()

        let remaining = records
        if remaining.isEmpty {
            dismiss()
        } else {
            let next = min(removingIndex, remaining.count - 1)
            selection = remaining[next].id
        }
    }

    private func deleteIndicator(_ i: Indicator) {
        if let asset = i.asset {
            try? FileVault.shared.remove(relativePath: asset.relativePath)
            ctx.delete(asset)
        }
        ctx.delete(i)
    }

    // MARK: - 复用件

    @ViewBuilder
    private func card<Content: View>(@ViewBuilder content: () -> Content) -> some View {
        VStack(alignment: .leading, spacing: 10) { content() }
            .padding(14)
            .frame(maxWidth: .infinity, alignment: .leading)
            .background(
                RoundedRectangle(cornerRadius: Tj.Radius.md, style: .continuous)
                    .fill(Tj.Palette.paper)
            )
            .overlay(
                RoundedRectangle(cornerRadius: Tj.Radius.md, style: .continuous)
                    .strokeBorder(Tj.Palette.lineSoft, lineWidth: 1)
            )
    }

    private func field(_ label: String, _ value: String) -> some View {
        HStack(alignment: .top, spacing: 12) {
            Text(label).font(.tjScaled(13)).foregroundStyle(Tj.Palette.text3)
            Spacer(minLength: 12)
            Text(value)
                .font(.tjScaled(14, weight: .medium))
                .foregroundStyle(Tj.Palette.text)
                .multilineTextAlignment(.trailing)
                .fixedSize(horizontal: false, vertical: true)
        }
    }

    @ViewBuilder
    private func evidenceButton(for indicator: Indicator) -> some View {
        if indicator.hasEvidenceBox,
           let page = indicator.sourcePageIndex,
           let assets = indicator.report?.assets,
           assets.indices.contains(page) {
            Button {
                evidenceTarget = indicator
            } label: {
                Label(String(appLoc: "查看原图位置"), systemImage: "viewfinder")
                    .font(.tjScaled(12, weight: .semibold))
                    .foregroundStyle(Tj.Palette.ink)
                    .padding(.horizontal, 10)
                    .padding(.vertical, 6)
                    .background(Capsule().fill(Tj.Palette.leaf.opacity(0.14)))
                    .contentShape(Rectangle())
            }
            .buttonStyle(.plain)
        }
    }

    private var divider: some View {
        Rectangle().fill(Tj.Palette.lineSoft).frame(height: 1)
    }

    private func statusChip(_ s: IndicatorStatus) -> some View {
        let text: String
        let color: Color
        let arrow: String
        switch s {
        case .high:   text = String(appLoc: "偏高"); color = Tj.Palette.brick; arrow = "↑"
        case .low:    text = String(appLoc: "偏低"); color = Tj.Palette.brick; arrow = "↓"
        case .normal: text = String(appLoc: "正常"); color = Tj.Palette.leaf;  arrow = ""
        }
        return HStack(spacing: 3) {
            if !arrow.isEmpty { Text(arrow).font(.tjScaled(11, weight: .bold)) }
            Text(text).font(.tjScaled(12, weight: .semibold))
        }
        .foregroundStyle(color)
        .padding(.horizontal, 8)
        .padding(.vertical, 4)
        .background(Capsule().fill(color.opacity(0.14)))
    }

    private nonisolated static func dateTimeText(_ d: Date) -> String {
        d.formatted(.dateTime.year().month().day().hour().minute())
    }
}
