import Testing
import SwiftData
import Foundation
@testable import 康康

struct CustomMonitorMetricTests {

    private func makeContainer() throws -> ModelContainer {
        let schema = Schema([
            CustomMonitorMetric.self,
            Indicator.self,
        ])
        let config = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true)
        return try ModelContainer(for: schema, configurations: [config])
    }

    @Test func seriesKeyAutoPrefixedWithCustom() {
        let m = CustomMonitorMetric(name: "腰围", unit: "cm")
        #expect(m.seriesKey.hasPrefix("custom."))
        #expect(m.seriesKey.count > "custom.".count)
    }

    @Test func seriesKeyUniquePerInstance() {
        let a = CustomMonitorMetric(name: "腰围", unit: "cm")
        let b = CustomMonitorMetric(name: "腰围", unit: "cm")
        #expect(a.seriesKey != b.seriesKey)
    }

    @Test func referenceRangeNilWhenBoundsAbsentOrInverted() {
        let none = CustomMonitorMetric(name: "x", unit: "")
        #expect(none.referenceRange == nil)

        let inverted = CustomMonitorMetric(name: "x", unit: "", lowerBound: 100, upperBound: 50)
        #expect(inverted.referenceRange == nil)

        let valid = CustomMonitorMetric(name: "x", unit: "", lowerBound: 60, upperBound: 100)
        #expect(valid.referenceRange == 60...100)
    }

    @Test func rangeTextFormattingDropsTrailingZero() {
        let intRange = CustomMonitorMetric(name: "x", unit: "cm",
                                            lowerBound: 70, upperBound: 90)
        #expect(intRange.rangeText == "70 - 90")

        let decimalRange = CustomMonitorMetric(name: "y", unit: "kg",
                                                lowerBound: 60.5, upperBound: 65.5)
        #expect(decimalRange.rangeText == "60.5 - 65.5")
    }

    @Test func roundtripsThroughSwiftData() throws {
        let container = try makeContainer()
        let ctx = ModelContext(container)
        let m = CustomMonitorMetric(name: "腰围", unit: "cm",
                                     lowerBound: 70, upperBound: 90,
                                     icon: "flame.fill")
        ctx.insert(m)
        try ctx.save()

        let fetched = try #require(try ctx.fetch(FetchDescriptor<CustomMonitorMetric>()).first)
        #expect(fetched.name == "腰围")
        #expect(fetched.unit == "cm")
        #expect(fetched.lowerBound == 70)
        #expect(fetched.upperBound == 90)
        #expect(fetched.icon == "flame.fill")
        #expect(fetched.seriesKey.hasPrefix("custom."))
    }

    @Test func seriesBucketResolvesCustomTitleAndRange() {
        let custom = CustomMonitorMetric(name: "腰围", unit: "cm",
                                          lowerBound: 70, upperBound: 90)
        let key = custom.seriesKey
        let now = Date()
        let day = { (offset: Int) -> Date in
            Calendar.current.date(byAdding: .day, value: offset, to: now)!
        }
        let items = [
            Indicator(name: "腰围", value: "80", unit: "cm", range: "70-90",
                      status: .normal, capturedAt: day(-2), seriesKey: key),
            Indicator(name: "腰围", value: "82", unit: "cm", range: "70-90",
                      status: .normal, capturedAt: day(-1), seriesKey: key),
        ]
        let buckets = SeriesBucket.build(from: items, customMetrics: [custom])

        #expect(buckets.count == 1)
        let b = try! #require(buckets.first)
        #expect(b.title == "腰围")
        #expect(b.unit == "cm")
        #expect(b.lines.first?.referenceRange == 70...90)
    }

    @Test func nameConflictEmptyNameYieldsNone() {
        let result = detectNameConflict(candidate: "  ", customs: [])
        #expect(result == .none)
    }

    @Test func nameConflictDetectsBuiltinMatch() {
        let result = detectNameConflict(candidate: "血压", customs: [])
        #expect(result == .builtin("血压"))
    }

    @Test func nameConflictBuiltinIgnoresWhitespace() {
        let result = detectNameConflict(candidate: "  空腹血糖  ", customs: [])
        #expect(result == .builtin("空腹血糖"))
    }

    @Test func nameConflictDetectsExistingCustom() {
        let existing = CustomMonitorMetric(name: "腰围", unit: "cm")
        let result = detectNameConflict(candidate: "腰围", customs: [existing])
        #expect(result == .existingCustom("腰围"))
    }

    @Test func nameConflictAllowsRenamingSelf() {
        // 编辑自己时,即使没改名也不应该报冲突
        let me = CustomMonitorMetric(name: "腰围", unit: "cm")
        let result = detectNameConflict(
            candidate: "腰围",
            customs: [me],
            excludingSeriesKey: me.seriesKey
        )
        #expect(result == .none)
    }

    @Test func nameConflictUnique() {
        let result = detectNameConflict(candidate: "步数", customs: [])
        #expect(result == .none)
    }

    @Test func seriesBucketFallsBackToIndicatorNameWhenCustomMissing() {
        // 用户删了 CustomMonitorMetric 但 Indicator 还在 → title fallback 到 indicator.name
        let orphanKey = "custom.deleted-xxxxx"
        let now = Date()
        let items = [
            Indicator(name: "睡眠时长", value: "7", unit: "h", range: "",
                      status: .normal, capturedAt: now, seriesKey: orphanKey),
            Indicator(name: "睡眠时长", value: "8", unit: "h", range: "",
                      status: .normal, capturedAt: now.addingTimeInterval(60),
                      seriesKey: orphanKey),
        ]
        let buckets = SeriesBucket.build(from: items, customMetrics: [])
        let b = try! #require(buckets.first)
        #expect(b.title == "睡眠时长")
        #expect(b.unit == "h")
        #expect(b.lines.first?.referenceRange == nil)
    }
}
