import SwiftUI
import SwiftData
import Charts

/// 趋势详情:大图表 + 时间范围筛选 + 统计摘要 + 数据点列表(点击跳当日详情)。
struct TrendDetailView: View {
    let bucket: SeriesBucket

    @Query(sort: \Indicator.capturedAt, order: .reverse)
    private var indicators: [Indicator]
    @Query(sort: \Report.reportDate, order: .reverse)
    private var reports: [Report]
    @Query(sort: \DiaryEntry.createdAt, order: .reverse)
    private var diaries: [DiaryEntry]
    @Query(sort: \Symptom.startedAt, order: .reverse)
    private var symptoms: [Symptom]

    @State private var range: TrendRange = .all
    @State private var openDay: SelectedDay?

    private let calendar = Calendar.current

    // MARK: 时间范围裁剪

    /// 锚点 = 最新一条记录的时间(数据稀疏时,"近3月"从最新记录倒推更有用)。
    private var anchorDate: Date {
        bucket.lines.flatMap(\.points).map(\.date).max() ?? .now
    }

    private var fullSpanDays: Int {
        let dates = bucket.lines.flatMap(\.points).map(\.date)
        guard let lo = dates.min(), let hi = dates.max() else { return 0 }
        return calendar.dateComponents([.day], from: lo, to: hi).day ?? 0
    }

    private var availableRanges: [TrendRange] {
        TrendRange.allCases.filter { r in
            guard let d = r.days else { return true } // .all 总显示
            return d < fullSpanDays
        }
    }

    private func filtered(_ line: SeriesBucket.SeriesLine) -> [SeriesBucket.Point] {
        guard let days = range.days,
              let cutoff = calendar.date(byAdding: .day, value: -days, to: anchorDate) else {
            return line.points
        }
        return line.points.filter { $0.date >= cutoff }
    }

    private var filteredLines: [SeriesBucket.SeriesLine] {
        bucket.lines.map { line in
            SeriesBucket.SeriesLine(
                id: line.id,
                seriesKey: line.seriesKey,
                label: line.label,
                color: line.color,
                points: filtered(line),
                referenceRange: line.referenceRange
            )
        }
    }

    var body: some View {
        ScrollView(showsIndicators: false) {
            VStack(alignment: .leading, spacing: 18) {
                if availableRanges.count > 1 {
                    rangePicker
                }
                chartCard
                statsCard
                TrendInsightCard(bucket: bucket)
                pointsList
            }
            .padding(.horizontal, 20)
            .padding(.vertical, 16)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
        .background(Tj.Palette.sand.ignoresSafeArea())
        .navigationTitle(bucket.title)
        .navigationBarTitleDisplayMode(.inline)
        .sheet(item: $openDay) { day in
            DayDetailSheet(
                date: day.date,
                indicators: indicators,
                reports: reports,
                diaries: diaries,
                symptoms: symptoms
            )
        }
    }

    // MARK: 时间范围切换

    private var rangePicker: some View {
        HStack(spacing: 0) {
            ForEach(availableRanges) { r in
                Button {
                    withAnimation(.snappy(duration: 0.2)) { range = r }
                } label: {
                    Text(r.label)
                        .font(.tjScaled( 12, weight: range == r ? .semibold : .regular))
                        .foregroundStyle(range == r ? Tj.Palette.paper : Tj.Palette.text)
                        .frame(maxWidth: .infinity)
                        .padding(.vertical, 7)
                        .background(Capsule().fill(range == r ? Tj.Palette.ink : Color.clear))
                }
                .buttonStyle(.plain)
            }
        }
        .padding(3)
        .background(Capsule().fill(Tj.Palette.paper))
        .overlay(Capsule().strokeBorder(Tj.Palette.line, lineWidth: 1))
    }

    // MARK: 大图表

    private var allFilteredPoints: [(line: SeriesBucket.SeriesLine, point: SeriesBucket.Point)] {
        filteredLines.flatMap { line in line.points.map { (line, $0) } }
    }

    private var dateDomain: ClosedRange<Date>? {
        let dates = allFilteredPoints.map(\.point.date)
        guard let lo = dates.min(), let hi = dates.max() else { return nil }
        if lo == hi {
            let earlier = calendar.date(byAdding: .hour, value: -12, to: lo) ?? lo
            let later = calendar.date(byAdding: .hour, value: 12, to: hi) ?? hi
            return earlier...later
        }
        return lo...hi
    }

    private var valueDomain: ClosedRange<Double>? {
        var lo = Double.greatestFiniteMagnitude
        var hi = -Double.greatestFiniteMagnitude
        for (_, p) in allFilteredPoints {
            lo = min(lo, p.value); hi = max(hi, p.value)
        }
        for line in filteredLines {
            if let r = line.referenceRange {
                lo = min(lo, r.lowerBound); hi = max(hi, r.upperBound)
            }
        }
        guard lo <= hi else { return nil }
        let span = hi - lo
        let pad = span > 0 ? max(1, span * 0.12) : max(1, abs(lo) * 0.1)
        return (lo - pad)...(hi + pad)
    }

    private var chartCard: some View {
        VStack(alignment: .leading, spacing: 12) {
            chart.frame(height: 220)
            if filteredLines.count > 1 {
                legendLine
            }
        }
        .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 var chart: some View {
        Chart {
            ForEach(filteredLines) { line in
                if let r = line.referenceRange, let dom = dateDomain {
                    RectangleMark(
                        xStart: .value("start", dom.lowerBound),
                        xEnd: .value("end", dom.upperBound),
                        yStart: .value("lo", r.lowerBound),
                        yEnd: .value("hi", r.upperBound)
                    )
                    .foregroundStyle(line.color.opacity(0.08))
                }
            }
            // 单条线时,线下垫一层渐变面积,增加体量、柔化观感
            //(多条线如血压不加,避免两片面积互相盖住)。
            if filteredLines.count == 1, let line = filteredLines.first {
                ForEach(line.points) { p in
                    // 显式把基线钉在值域下界:用单值 AreaMark 会以隐式基线(0/域外)兜底,
                    // 渐变被拉到可视区外几乎不淡出,看着像一块实色底色一路铺到图表底。
                    AreaMark(
                        x: .value("时间", p.date),
                        yStart: .value("基线", (valueDomain ?? 0...1).lowerBound),
                        yEnd: .value(line.label ?? bucket.title, p.value)
                    )
                    .foregroundStyle(LinearGradient(
                        colors: [line.color.opacity(0.16), line.color.opacity(0)],
                        startPoint: .top, endPoint: .bottom))
                    .interpolationMethod(.monotone)
                }
            }
            ForEach(filteredLines) { line in
                ForEach(line.points) { p in
                    LineMark(
                        x: .value("时间", p.date),
                        y: .value(line.label ?? bucket.title, p.value),
                        series: .value("series", line.id)
                    )
                    .foregroundStyle(line.color)
                    // monotone:平滑但不在尖峰处过冲鼓包,更贴合真实读数。
                    .interpolationMethod(.monotone)
                    // 圆角端点 + 连接,去掉折线的生硬尖角。
                    .lineStyle(StrokeStyle(lineWidth: 2, lineCap: .round, lineJoin: .round))
                    PointMark(
                        x: .value("时间", p.date),
                        y: .value(line.label ?? bucket.title, p.value)
                    )
                    .foregroundStyle(p.status == .normal ? line.color : Tj.Palette.brick)
                    .symbolSize(p.status == .normal ? 26 : 44)
                }
            }
        }
        .chartXAxis {
            AxisMarks(values: .automatic(desiredCount: 4)) { _ in
                AxisGridLine().foregroundStyle(Tj.Palette.lineSoft)
                AxisValueLabel(format: .dateTime.month(.abbreviated).day())
                    .foregroundStyle(Tj.Palette.text3)
            }
        }
        .chartYAxis {
            AxisMarks(position: .leading, values: .automatic(desiredCount: 4)) { _ in
                AxisGridLine().foregroundStyle(Tj.Palette.lineSoft)
                AxisValueLabel()
                    .foregroundStyle(Tj.Palette.text3)
                    .font(.tjScaled( 10, design: .monospaced))
            }
        }
        .chartYScale(domain: valueDomain ?? 0...1)
    }

    private var legendLine: some View {
        HStack(spacing: 14) {
            ForEach(filteredLines) { line in
                HStack(spacing: 5) {
                    Circle().fill(line.color).frame(width: 8, height: 8)
                    Text(line.label ?? line.seriesKey)
                        .font(.tjScaled( 11))
                        .foregroundStyle(Tj.Palette.text2)
                }
            }
        }
    }

    // MARK: 统计摘要

    private var statsCard: some View {
        VStack(alignment: .leading, spacing: 14) {
            ForEach(filteredLines) { line in
                lineStats(line)
                if line.id != filteredLines.last?.id {
                    Divider().overlay(Tj.Palette.lineSoft)
                }
            }
        }
        .padding(16)
        .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)
        )
    }

    @ViewBuilder
    private func lineStats(_ line: SeriesBucket.SeriesLine) -> some View {
        let pts = line.points
        let values = pts.map(\.value)
        let latest = pts.last
        let prev = pts.count >= 2 ? pts[pts.count - 2] : nil
        let minV = values.min() ?? 0
        let maxV = values.max() ?? 0
        let avg = values.isEmpty ? 0 : values.reduce(0, +) / Double(values.count)

        VStack(alignment: .leading, spacing: 10) {
            if filteredLines.count > 1, let label = line.label {
                Text(label)
                    .font(.tjScaled( 12, weight: .semibold))
                    .foregroundStyle(Tj.Palette.text2)
            }
            HStack(alignment: .firstTextBaseline, spacing: 6) {
                Text(latest.map { fmt($0.value) } ?? "—")
                    .font(.tjScaled( 28, weight: .bold, design: .monospaced))
                    .foregroundStyle((latest?.status ?? .normal) == .normal ? Tj.Palette.text : Tj.Palette.brick)
                Text(bucket.unit)
                    .font(.tjScaled( 12))
                    .foregroundStyle(Tj.Palette.text3)
                Spacer()
                if let delta = deltaText(latest: latest, prev: prev) {
                    Text(delta.text)
                        .font(.tjScaled( 13, weight: .semibold, design: .monospaced))
                        .foregroundStyle(delta.color)
                }
            }
            HStack(spacing: 0) {
                statCell(String(appLoc: "最低"), fmt(minV))
                statCell(String(appLoc: "最高"), fmt(maxV))
                statCell(String(appLoc: "平均"), fmt(avg))
                statCell(String(appLoc: "记录"), "\(pts.count)")
            }
        }
    }

    private func statCell(_ label: String, _ value: String) -> some View {
        VStack(spacing: 3) {
            Text(value)
                .font(.tjScaled( 14, weight: .semibold, design: .monospaced))
                .foregroundStyle(Tj.Palette.text)
            Text(label)
                .font(.tjScaled( 10))
                .foregroundStyle(Tj.Palette.text3)
        }
        .frame(maxWidth: .infinity)
    }

    /// 对比上次:Δ 绝对值 + 百分比 + 升降箭头;跨参考范围边界标红。
    private func deltaText(latest: SeriesBucket.Point?,
                           prev: SeriesBucket.Point?) -> (text: String, color: Color)? {
        guard let latest, let prev else { return nil }
        let d = latest.value - prev.value
        let arrow = d > 0 ? "↑" : (d < 0 ? "↓" : "→")
        let pct = prev.value != 0 ? abs(d / prev.value) * 100 : 0
        let abnormalShift = (prev.status == .normal) != (latest.status == .normal)
        let color: Color = abnormalShift
            ? Tj.Palette.brick
            : (d == 0 ? Tj.Palette.text3 : Tj.Palette.text2)
        let pctStr = pct > 0 ? String(format: " (%.0f%%)", pct) : ""
        return ("\(arrow) \(fmt(abs(d)))\(pctStr)", color)
    }

    // MARK: 数据点列表

    /// 跨线按天合并:每天一行,展示该天各线的值。倒序。
    private var pointRows: [PointRow] {
        var byDay: [Date: [String: SeriesBucket.Point]] = [:]
        for line in filteredLines {
            for p in line.points {
                let day = calendar.startOfDay(for: p.date)
                byDay[day, default: [:]][line.id] = p
            }
        }
        return byDay.keys.sorted(by: >).map { day in
            PointRow(day: day, byLine: byDay[day] ?? [:])
        }
    }

    private struct PointRow: Identifiable {
        let day: Date
        let byLine: [String: SeriesBucket.Point]
        var id: TimeInterval { day.timeIntervalSince1970 }
    }

    private var pointsList: some View {
        VStack(alignment: .leading, spacing: 10) {
            Text("全部记录")
                .font(.tjScaled( 13, weight: .semibold))
                .foregroundStyle(Tj.Palette.text2)
            VStack(spacing: 8) {
                ForEach(pointRows) { row in
                    Button {
                        openDay = SelectedDay(date: row.day)
                    } label: {
                        pointRowView(row)
                    }
                    .buttonStyle(.plain)
                }
            }
        }
    }

    private func pointRowView(_ row: PointRow) -> some View {
        HStack(spacing: 12) {
            Text(row.day.formatted(.dateTime.year().month(.abbreviated).day()))
                .font(.tjScaled( 13))
                .foregroundStyle(Tj.Palette.text2)
            Spacer(minLength: 8)
            HStack(spacing: 10) {
                ForEach(filteredLines) { line in
                    if let p = row.byLine[line.id] {
                        HStack(spacing: 3) {
                            if filteredLines.count > 1 {
                                Circle().fill(line.color).frame(width: 6, height: 6)
                            }
                            Text(fmt(p.value) + arrow(p.status))
                                .font(.tjScaled( 13, weight: .semibold, design: .monospaced))
                                .foregroundStyle(p.status == .normal ? Tj.Palette.text : Tj.Palette.brick)
                        }
                    }
                }
            }
            Image(systemName: "chevron.right")
                .font(.tjScaled( 11, weight: .medium))
                .foregroundStyle(Tj.Palette.text3)
        }
        .padding(12)
        .frame(maxWidth: .infinity)
        .tjCard(bordered: true)
    }

    private func arrow(_ status: IndicatorStatus) -> String {
        switch status {
        case .high: return " ↑"
        case .low:  return " ↓"
        case .normal: return ""
        }
    }

    private func fmt(_ v: Double) -> String {
        v.truncatingRemainder(dividingBy: 1) == 0
            ? String(format: "%.0f", v)
            : String(format: "%.1f", v)
    }
}

// MARK: - AI 趋势解读卡

/// 进入页面先查指纹缓存:命中秒显;未命中本地现算(经 TrendInsightService,§3.1)。
private struct TrendInsightCard: View {
    let bucket: SeriesBucket
    @State private var text: String?
    @State private var running = false
    @State private var failedMessage: String?

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            HStack(spacing: 6) {
                Image(systemName: "sparkles")
                    .font(.tjScaled( 12))
                    .foregroundStyle(Tj.Palette.ink)
                Text("AI 解读")
                    .font(.tjScaled( 12, weight: .semibold))
                    .foregroundStyle(Tj.Palette.text2)
                Spacer()
                // 手动重新解读:任何时候都能点(解读中除外)。解决偶发不自动触发,
                // 也允许已生成后再跑一次拿最新解读。
                if !running {
                    Button { Task { await load(force: true) } } label: {
                        HStack(spacing: 4) {
                            Image(systemName: "arrow.clockwise")
                                .font(.tjScaled( 11, weight: .semibold))
                            Text(text == nil ? String(appLoc: "解读") : String(appLoc: "重新解读"))
                                .font(.tjScaled( 12, weight: .semibold))
                        }
                        .foregroundStyle(Tj.Palette.ink)
                        .padding(.horizontal, 10)
                        .padding(.vertical, 5)
                        .background(Capsule().fill(Tj.Palette.sand2))
                        .contentShape(Capsule())
                    }
                    .buttonStyle(.plain)
                }
            }
            if let text {
                Text(text)
                    .font(.tjScaled( 13))
                    .lineSpacing(3)
                    .foregroundStyle(Tj.Palette.text)
                    .fixedSize(horizontal: false, vertical: true)
                AIDisclaimerFooter()
            } else if running {
                Text("本地 AI 解读中…")
                    .font(.tjScaled( 12))
                    .foregroundStyle(Tj.Palette.text3)
                AIFlowBar()
            } else if let failedMessage {
                Text(failedMessage)
                    .font(.tjScaled( 12))
                    .foregroundStyle(Tj.Palette.text3)
            } else {
                // 空闲态(偶发没自动触发):给明确引导,点右上即可生成。
                Text("点右上「解读」生成本地趋势解读")
                    .font(.tjScaled( 12))
                    .foregroundStyle(Tj.Palette.text3)
            }
        }
        .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)
        )
        .task(id: bucket.id) { await load(force: false) }
    }

    @MainActor
    private func load(force: Bool) async {
        if !force, let cached = TrendInsightService.shared.cachedText(for: bucket) {
            text = cached
            return
        }
        running = true
        failedMessage = nil
        do {
            text = try await TrendInsightService.shared.generate(for: bucket)
        } catch {
            // 区分「模型没下载」与「下载了但这次推理没成功」,前者给下载引导(CLAUDE.md §4)。
            let downloaded = ModelStore.shared.isComplete(for: .mnnLLM) || ModelStore.shared.isComplete(for: .llm)
            failedMessage = downloaded
                ? String(appLoc: "本地推理这次没成功,点右上「解读」重试")
                : String(appLoc: "AI 解读需先在「我的 · 模型管理」下载模型")
        }
        running = false
    }
}

enum TrendRange: String, CaseIterable, Identifiable {
    case all, year, sixMonths, threeMonths
    var id: String { rawValue }

    var label: String {
        switch self {
        case .all:         return String(appLoc: "全部")
        case .year:        return String(appLoc: "近1年")
        case .sixMonths:   return String(appLoc: "近6月")
        case .threeMonths: return String(appLoc: "近3月")
        }
    }

    /// nil = 不裁剪。
    var days: Int? {
        switch self {
        case .all:         return nil
        case .year:        return 365
        case .sixMonths:   return 182
        case .threeMonths: return 91
        }
    }
}
