import Testing
import Foundation
@testable import 康康

/// `ExportTrendBuilder` 是纯函数,覆盖:方向判定、血压合并、相关性过滤、
/// 点数 <2 过滤、跨参考范围边界标记、非数值点丢弃、整行文案格式。
struct ExportTrendBuilderTests {

    private func ind(
        name: String = "血糖",
        value: String,
        unit: String = "mmol/L",
        range: String = "3.9-6.1",
        status: IndicatorStatus = .normal,
        daysAgo: Int,
        seriesKey: String? = nil
    ) -> Indicator {
        let date = Calendar.current.date(byAdding: .day, value: -daysAgo, to: .now)!
        return Indicator(name: name, value: value, unit: unit, range: range,
                         status: status, capturedAt: date, seriesKey: seriesKey)
    }

    @Test func upDirectionFlaggedAndLineFormat() {
        let items = [
            ind(value: "5.2", status: .normal, daysAgo: 27),
            ind(value: "6.8", status: .high, daysAgo: 0),
        ]
        let trends = ExportTrendBuilder.build(allInWindow: items, relevant: items)
        let t = try! #require(trends.first)
        #expect(t.direction == .up)
        #expect(t.flagged)            // 末值 high
        #expect(t.count == 2)
        #expect(t.valueText == "5.2→6.8")
        #expect(t.rangeText == "3.9-6.1")
        #expect(t.line() == "⚠️ 血糖 5.2→6.8 mmol/L ↑（参考 3.9-6.1），近 27 天 2 次")
    }

    @Test func downDirection() {
        let items = [
            ind(value: "6.0", daysAgo: 10),
            ind(value: "5.0", daysAgo: 1),
        ]
        let t = try! #require(ExportTrendBuilder.build(allInWindow: items, relevant: items).first)
        #expect(t.direction == .down)
        #expect(!t.flagged)           // 两点都 normal
    }

    @Test func flatWithinThreshold() {
        // (5.1-5.0)/5.0 = 0.02 < 0.05 → 平稳
        let items = [
            ind(value: "5.0", daysAgo: 5),
            ind(value: "5.1", daysAgo: 1),
        ]
        let t = try! #require(ExportTrendBuilder.build(allInWindow: items, relevant: items).first)
        #expect(t.direction == .flat)
    }

    @Test func filtersSeriesWithFewerThanTwoPoints() {
        let items = [ind(value: "5.0", daysAgo: 1)]
        #expect(ExportTrendBuilder.build(allInWindow: items, relevant: items).isEmpty)
    }

    @Test func excludesIrrelevantSeries() {
        let glucose = [
            ind(name: "血糖", value: "5.0", unit: "mmol/L", daysAgo: 3),
            ind(name: "血糖", value: "5.5", unit: "mmol/L", daysAgo: 1),
        ]
        let weight = [
            ind(name: "体重", value: "68", unit: "kg", range: "", daysAgo: 3),
            ind(name: "体重", value: "67", unit: "kg", range: "", daysAgo: 1),
        ]
        // weight 有 ≥2 点,但不在 relevant 集 → 不出趋势
        let trends = ExportTrendBuilder.build(allInWindow: glucose + weight, relevant: glucose)
        #expect(trends.count == 1)
        #expect(trends.first?.title == "血糖")
    }

    @Test func bloodPressureMergesToSingleLine() {
        let items = [
            ind(name: "收缩压", value: "150", unit: "mmHg", range: "", daysAgo: 20, seriesKey: "bp.systolic"),
            ind(name: "舒张压", value: "95",  unit: "mmHg", range: "", daysAgo: 20, seriesKey: "bp.diastolic"),
            ind(name: "收缩压", value: "138", unit: "mmHg", range: "", daysAgo: 1,  seriesKey: "bp.systolic"),
            ind(name: "舒张压", value: "88",  unit: "mmHg", range: "", daysAgo: 1,  seriesKey: "bp.diastolic"),
        ]
        let t = try! #require(ExportTrendBuilder.build(allInWindow: items, relevant: items).first)
        #expect(t.title == "血压")
        #expect(t.unit == "mmHg")
        #expect(t.valueText == "150/95→138/88")
        #expect(t.direction == .down)   // 收缩压为准
        #expect(t.rangeText == nil)      // 血压双范围不展示
    }

    @Test func flaggedWhenStatusCrossesBoundary() {
        // 首高末正常:跨参考范围边界 → 仍 flagged,提示医生注意变化
        let items = [
            ind(value: "6.8", status: .high, daysAgo: 5),
            ind(value: "5.5", status: .normal, daysAgo: 1),
        ]
        let t = try! #require(ExportTrendBuilder.build(allInWindow: items, relevant: items).first)
        #expect(t.flagged)
    }

    @Test func nonNumericPointDropped() {
        let items = [
            ind(value: "高", daysAgo: 3),
            ind(value: "5.0", daysAgo: 2),
            ind(value: "5.5", daysAgo: 1),
        ]
        let t = try! #require(ExportTrendBuilder.build(allInWindow: items, relevant: items).first)
        #expect(t.count == 2)   // "高" 解析失败被丢
    }
}
