import SwiftUI
import Charts

struct SeriesChartCard: View {
    let bucket: SeriesBucket

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

    private var dateDomain: ClosedRange<Date>? {
        let dates = allPoints.map(\.point.date)
        guard let lo = dates.min(), let hi = dates.max() else { return nil }
        if lo == hi {
            // 只有一个点的极端情况:扩 1 天显示
            let cal = Calendar.current
            let earlier = cal.date(byAdding: .hour, value: -12, to: lo) ?? lo
            let later = cal.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 allPoints {
            lo = min(lo, p.value)
            hi = max(hi, p.value)
        }
        for line in bucket.lines {
            if let r = line.referenceRange {
                lo = min(lo, r.lowerBound)
                hi = max(hi, r.upperBound)
            }
        }
        // 无数据时 lo>hi → nil;所有点同值(lo==hi)时按值本身对称留白,
        // 否则会落到 0...1 把数据点挤出可视域。
        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)
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            header
            chart
                .frame(height: 120)
            if bucket.lines.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 header: some View {
        HStack(alignment: .lastTextBaseline, spacing: 10) {
            Text(bucket.title)
                .font(.system(size: 15, weight: .semibold))
                .foregroundStyle(Tj.Palette.text)
            Text("\(allPoints.count) 条 · 近 \(daysSpanLabel)")
                .font(.system(size: 11))
                .foregroundStyle(Tj.Palette.text3)
            Spacer()
            latestValueBadge
        }
    }

    private var latestValueBadge: some View {
        let parts = bucket.lines.compactMap { line -> String? in
            guard let p = line.latestPoint else { return nil }
            return formatValue(p.value)
        }
        let joined = parts.joined(separator: " / ")
        let anyAbnormal = bucket.lines.contains { line in
            (line.latestPoint?.status ?? .normal) != .normal
        }
        return HStack(spacing: 4) {
            Text(joined)
                .font(.system(size: 14, weight: .semibold, design: .monospaced))
                .foregroundStyle(anyAbnormal ? Tj.Palette.brick : Tj.Palette.text)
            Text(bucket.unit)
                .font(.system(size: 10, design: .monospaced))
                .foregroundStyle(Tj.Palette.text3)
        }
    }

    private var chart: some View {
        Chart {
            // 参考范围带
            ForEach(bucket.lines) { 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))
                }
            }

            // 折线 + 点
            ForEach(bucket.lines) { line in
                ForEach(line.points) { p in
                    LineMark(
                        x: .value("时间", p.date),
                        y: .value(line.label ?? bucket.title, p.value)
                    )
                    .foregroundStyle(line.color)
                    .interpolationMethod(.catmullRom)
                    .lineStyle(StrokeStyle(lineWidth: 2))
                }
                .symbol {
                    Circle()
                        .fill(line.color)
                        .frame(width: 6, height: 6)
                }
            }
        }
        .chartXAxis {
            AxisMarks(values: .automatic(desiredCount: 4)) { _ in
                AxisGridLine().foregroundStyle(Tj.Palette.lineSoft)
                AxisValueLabel(format: .dateTime.month(.abbreviated).day(),
                               centered: false)
                    .foregroundStyle(Tj.Palette.text3)
            }
        }
        .chartYAxis {
            AxisMarks(position: .leading, values: .automatic(desiredCount: 3)) { _ in
                AxisGridLine().foregroundStyle(Tj.Palette.lineSoft)
                AxisValueLabel()
                    .foregroundStyle(Tj.Palette.text3)
                    .font(.system(size: 10, design: .monospaced))
            }
        }
        .chartYScale(domain: valueDomain ?? 0...1)
    }

    private var legendLine: some View {
        HStack(spacing: 12) {
            ForEach(bucket.lines) { line in
                HStack(spacing: 4) {
                    Circle()
                        .fill(line.color)
                        .frame(width: 8, height: 8)
                    Text(line.label ?? line.seriesKey)
                        .font(.system(size: 11))
                        .foregroundStyle(Tj.Palette.text2)
                }
            }
        }
    }

    private var daysSpanLabel: String {
        guard let dom = dateDomain else { return "—" }
        let days = Calendar.current.dateComponents([.day],
                                                    from: dom.lowerBound,
                                                    to: dom.upperBound).day ?? 0
        if days <= 0 { return String(appLoc: "今天") }
        if days < 30 { return String(appLoc: "\(days) 天") }
        if days < 365 { return String(appLoc: "\(days / 30) 个月") }
        return String(appLoc: "\(days / 365) 年")
    }

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