import SwiftUI
import SwiftData
import Foundation

/// 趋势桶的来源类别。
/// - `.monitor`:长期监测预设 / 自定义 / 血压(按 seriesKey 分组)。
/// - `.lab`:任意出现 ≥2 次的化验/手动/报告指标(按 name+unit 分组,无 seriesKey)。
enum SeriesKind { case monitor, lab }

/// 长期监测系列在 Trends 折线图里的展示桶。
/// 单系列(血糖/体重/...)= 1 个 SeriesLine;血压特殊 = 收缩 + 舒张 2 条线同卡。
struct SeriesBucket: Identifiable {
    let id: String
    let title: String
    let unit: String
    let lines: [SeriesLine]
    let latestDate: Date
    let kind: SeriesKind

    struct SeriesLine: Identifiable {
        let id: String
        let seriesKey: String
        let label: String?
        let color: Color
        let points: [Point]
        let referenceRange: ClosedRange<Double>?

        var latestPoint: Point? { points.last }
    }

    struct Point: Identifiable, Hashable {
        let id: String
        let date: Date
        let value: Double
        let status: IndicatorStatus
    }
}

extension SeriesBucket {
    /// 把全表 Indicator(无 seriesKey 的会被跳过)折成 SeriesBucket 列表。
    /// 同 seriesKey 内按 capturedAt 升序;BP 两个 key 合并成一个 bucket;
    /// `minPoints` 以下的系列不返回,默认 2(单点不画线)。
    static func build(from indicators: [Indicator],
                      profile: UserProfile? = nil,
                      customMetrics: [CustomMonitorMetric] = [],
                      minPoints: Int = 2) -> [SeriesBucket] {
        var buckets: [String: [Indicator]] = [:]
        for i in indicators {
            guard let key = i.seriesKey, !key.isEmpty else { continue }
            buckets[key, default: []].append(i)
        }

        // 合并血压
        let bpKeys: Set<String> = ["bp.systolic", "bp.diastolic"]
        let bpHasEnoughPoints = (buckets["bp.systolic"]?.count ?? 0) >= minPoints

        var results: [SeriesBucket] = []

        if bpHasEnoughPoints {
            results.append(buildBP(from: buckets, profile: profile))
        }
        for k in bpKeys { buckets.removeValue(forKey: k) }

        let customByKey: [String: CustomMonitorMetric] = Dictionary(
            uniqueKeysWithValues: customMetrics.map { ($0.seriesKey, $0) }
        )

        for (key, items) in buckets {
            guard items.count >= minPoints else { continue }
            if let bucket = buildSingle(key: key, items: items,
                                        profile: profile,
                                        custom: customByKey[key]) {
                results.append(bucket)
            }
        }

        // —— lab 段:任何没有 seriesKey 的指标,按 name+unit 归并;同名出现 ≥minPoints 次即成趋势。
        var labBuckets: [String: [Indicator]] = [:]
        for i in indicators {
            if let key = i.seriesKey, !key.isEmpty { continue } // seriesKey 指标只进 monitor 段
            let nk = normalizedKey(name: i.name, unit: i.unit)
            guard !nk.isEmpty else { continue }
            labBuckets[nk, default: []].append(i)
        }
        for (_, items) in labBuckets {
            guard items.count >= minPoints else { continue }
            if let bucket = buildLab(items: items) {
                results.append(bucket)
            }
        }

        return results.sorted { $0.latestDate > $1.latestDate }
    }

    /// name+unit 归一化:trim + 小写 + 折叠内部空白。空名返回空串(调用方跳过)。
    static func normalizedKey(name: String, unit: String) -> String {
        func norm(_ s: String) -> String {
            s.trimmingCharacters(in: .whitespacesAndNewlines)
                .lowercased()
                .components(separatedBy: .whitespacesAndNewlines)
                .filter { !$0.isEmpty }
                .joined(separator: " ")
        }
        let n = norm(name)
        guard !n.isEmpty else { return "" }
        return n + "|" + norm(unit)
    }

    /// 解析参考范围字符串 → ClosedRange。支持 "3.9-6.1" / "3.9~6.1" / "3.9 - 6.1"。
    /// 单边("<5.2" / ">40" / "≤120")暂返回 nil(图不画带,正常)。
    static func parseRange(_ raw: String) -> ClosedRange<Double>? {
        let s = raw.replacingOccurrences(of: "～", with: "~")
                   .replacingOccurrences(of: "~", with: "-")
        guard let regex = try? NSRegularExpression(
            pattern: #"(\d+(?:\.\d+)?)\s*-\s*(\d+(?:\.\d+)?)"#
        ) else { return nil }
        let range = NSRange(s.startIndex..<s.endIndex, in: s)
        guard let m = regex.firstMatch(in: s, range: range),
              let r1 = Range(m.range(at: 1), in: s),
              let r2 = Range(m.range(at: 2), in: s),
              let lo = Double(s[r1]), let hi = Double(s[r2]),
              lo <= hi else { return nil }
        return lo...hi
    }

    private static func buildLab(items: [Indicator]) -> SeriesBucket? {
        let sorted = items.sorted { $0.capturedAt < $1.capturedAt }
        guard let latest = sorted.last else { return nil }
        let points = sorted.compactMap { point(from: $0) }
        guard points.count >= 2 else { return nil } // 值无法解析为数字的会被丢弃,可能不足 2 点

        let line = SeriesLine(
            id: "lab:\(latest.name)",
            seriesKey: "lab:\(latest.name)",
            label: nil,
            color: Tj.Palette.teal,
            points: points,
            referenceRange: parseRange(latest.range)
        )
        return SeriesBucket(
            id: "lab:\(normalizedKey(name: latest.name, unit: latest.unit))",
            title: latest.name,
            unit: latest.unit,
            lines: [line],
            latestDate: latest.capturedAt,
            kind: .lab
        )
    }

    private static func buildSingle(key: String,
                                    items: [Indicator],
                                    profile: UserProfile?,
                                    custom: CustomMonitorMetric? = nil) -> SeriesBucket? {
        let sorted = items.sorted { $0.capturedAt < $1.capturedAt }
        guard let latest = sorted.last else { return nil }

        // 优先 custom,其次 builtin metric,最后 fallback 到 Indicator 自身
        let metric = monitorMetric(for: key)
        let field = metric?.fields.first { $0.seriesKey == key }
        let title = custom?.name
            ?? metric?.displayName
            ?? sorted.first?.name
            ?? key
        let unit = custom?.unit.nonEmptyOr(nil)
            ?? field?.unit
            ?? sorted.first?.unit
            ?? ""
        let range = custom?.referenceRange
            ?? field.flatMap { metric?.effectiveRange(for: $0, profile: profile) }

        let line = SeriesLine(
            id: key,
            seriesKey: key,
            label: nil,
            color: Tj.Palette.teal,
            points: sorted.compactMap { point(from: $0) },
            referenceRange: range
        )

        return SeriesBucket(
            id: key,
            title: title,
            unit: unit,
            lines: [line],
            latestDate: latest.capturedAt,
            kind: .monitor
        )
    }

    private static func buildBP(from buckets: [String: [Indicator]],
                                profile: UserProfile?) -> SeriesBucket {
        let m = MonitorMetric.bloodPressure
        let sysField = m.fields[0]
        let diaField = m.fields[1]

        let sysItems = (buckets["bp.systolic"] ?? []).sorted { $0.capturedAt < $1.capturedAt }
        let diaItems = (buckets["bp.diastolic"] ?? []).sorted { $0.capturedAt < $1.capturedAt }

        let sysLine = SeriesLine(
            id: "bp.systolic",
            seriesKey: "bp.systolic",
            label: String(appLoc: "收缩"),
            color: Tj.Palette.teal,
            points: sysItems.compactMap { point(from: $0) },
            referenceRange: m.effectiveRange(for: sysField, profile: profile)
        )
        let diaLine = SeriesLine(
            id: "bp.diastolic",
            seriesKey: "bp.diastolic",
            label: String(appLoc: "舒张"),
            color: Tj.Palette.leaf,
            points: diaItems.compactMap { point(from: $0) },
            referenceRange: m.effectiveRange(for: diaField, profile: profile)
        )

        let latest = max(
            sysItems.last?.capturedAt ?? .distantPast,
            diaItems.last?.capturedAt ?? .distantPast
        )

        // 只保留有数据点的线:某一路(收缩/舒张)为空时不画空线 + 残缺图例。
        let lines = [sysLine, diaLine].filter { !$0.points.isEmpty }
        return SeriesBucket(
            id: "bp",
            title: String(appLoc: "血压"),
            unit: "mmHg",
            lines: lines,
            latestDate: latest,
            kind: .monitor
        )
    }

    private static func point(from i: Indicator) -> Point? {
        guard let v = Double(i.value.trimmingCharacters(in: .whitespaces)) else { return nil }
        return Point(
            id: "\(i.persistentModelID)",
            date: i.capturedAt,
            value: v,
            status: i.status
        )
    }

    private static func monitorMetric(for seriesKey: String) -> MonitorMetric? {
        MonitorMetric.allCases.first { m in
            m.fields.contains { $0.seriesKey == seriesKey }
        }
    }
}

private extension String {
    /// 空串 → fallback;非空 → 自身。
    func nonEmptyOr(_ fallback: String?) -> String? {
        trimmingCharacters(in: .whitespaces).isEmpty ? fallback : self
    }
}
