import Testing
import SwiftData
import Foundation
import CoreGraphics
@testable import 康康

struct ModelsSchemaTests {

    private func makeContainer() throws -> ModelContainer {
        let schema = Schema([
            Indicator.self,
            Report.self,
            DiaryEntry.self,
            Asset.self,
            ChatTurn.self,
            Symptom.self,
            UserProfile.self,
            MetricReminder.self,
            CustomMonitorMetric.self,
            Medication.self,
        ])
        let config = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true)
        return try ModelContainer(for: schema, configurations: [config])
    }

    @Test func insertIndicatorWithReportRelationship() throws {
        let container = try makeContainer()
        let ctx = ModelContext(container)

        let report = Report(title: "春检", type: .checkup, reportDate: .now)
        let indicator = Indicator(
            name: "ALT",
            value: "32",
            unit: "U/L",
            range: "9-50",
            status: .normal,
            report: report
        )
        ctx.insert(report)
        ctx.insert(indicator)
        try ctx.save()

        #expect(report.indicators.count == 1)
        #expect(indicator.report?.title == "春检")
    }

    @Test func cascadeDeleteReportRemovesIndicators() throws {
        let container = try makeContainer()
        let ctx = ModelContext(container)

        let report = Report(title: "春检", type: .checkup, reportDate: .now)
        let indicator = Indicator(
            name: "ALT", value: "32", unit: "U/L", range: "9-50",
            status: .normal, report: report
        )
        ctx.insert(report)
        ctx.insert(indicator)
        try ctx.save()

        ctx.delete(report)
        try ctx.save()

        let remaining = try ctx.fetch(FetchDescriptor<Indicator>())
        #expect(remaining.isEmpty)
    }

    @Test func ongoingSymptomQueryFiltersByEndedAt() throws {
        let container = try makeContainer()
        let ctx = ModelContext(container)

        let active = Symptom(name: "头痛", startedAt: .now.addingTimeInterval(-3600))
        let ended = Symptom(
            name: "咳嗽",
            startedAt: .now.addingTimeInterval(-7200),
            endedAt: .now.addingTimeInterval(-1800)
        )
        ctx.insert(active)
        ctx.insert(ended)
        try ctx.save()

        let predicate = #Predicate<Symptom> { $0.endedAt == nil }
        let ongoing = try ctx.fetch(FetchDescriptor<Symptom>(predicate: predicate))

        #expect(ongoing.count == 1)
        #expect(ongoing.first?.name == "头痛")
        #expect(active.isOngoing)
        #expect(!ended.isOngoing)
        #expect(active.duration >= 3600)
    }

    @Test func symptomSeverityClampedToRange() throws {
        let high = Symptom(name: "腹痛", severity: 99)
        let low = Symptom(name: "失眠", severity: -3)
        #expect(high.severity == 5)
        #expect(low.severity == 1)
    }

    @Test func chatTurnPersistsReferencedIDs() throws {
        let container = try makeContainer()
        let ctx = ModelContext(container)

        let turn = ChatTurn(
            question: "我的 LDL 怎么样?",
            answer: "近 3 个月 LDL 偏高 [1]",
            referencedIndicatorIDs: ["abc"],
            referencedReportIDs: [],
            decodeRate: 24.3
        )
        ctx.insert(turn)
        try ctx.save()

        let all = try ctx.fetch(FetchDescriptor<ChatTurn>())
        #expect(all.count == 1)
        #expect(all.first?.referencedIndicatorIDs == ["abc"])
    }

    @Test func indicatorSeriesKeyRoundtrip() throws {
        let container = try makeContainer()
        let ctx = ModelContext(container)

        let bp = Indicator(
            name: "收缩压",
            value: "125",
            unit: "mmHg",
            range: "90-140",
            status: .normal,
            pinned: true,
            seriesKey: "bp.systolic"
        )
        ctx.insert(bp)
        try ctx.save()

        let fetched = try #require(try ctx.fetch(FetchDescriptor<Indicator>()).first)
        #expect(fetched.seriesKey == "bp.systolic")
        #expect(fetched.pinned == true)
    }

    @Test func indicatorSeriesKeyDefaultsToNil() {
        let i = Indicator(name: "ALT", value: "32", unit: "U/L", range: "9-50", status: .normal)
        #expect(i.seriesKey == nil)
    }

    @Test func indicatorEvidenceLocationRoundtrip() throws {
        let container = try makeContainer()
        let ctx = ModelContext(container)

        let indicator = Indicator(
            name: "尿酸",
            value: "486",
            unit: "μmol/L",
            range: "208 - 428",
            status: .high,
            source: .report,
            sourcePageIndex: 1,
            sourceBoxX: 0.18,
            sourceBoxY: 0.42,
            sourceBoxWidth: 0.68,
            sourceBoxHeight: 0.08
        )
        ctx.insert(indicator)
        try ctx.save()

        let fetched = try #require(try ctx.fetch(FetchDescriptor<Indicator>()).first)
        #expect(fetched.sourcePageIndex == 1)
        #expect(fetched.sourceBoxX == 0.18)
        #expect(fetched.sourceBoxY == 0.42)
        #expect(fetched.sourceBoxWidth == 0.68)
        #expect(fetched.sourceBoxHeight == 0.08)
        #expect(fetched.hasEvidenceBox)
        #expect(fetched.evidenceRect?.width == 0.68)
    }

    @Test func userProfileSchemaPersistsAcrossSave() throws {
        let container = try makeContainer()
        let ctx = ModelContext(container)

        let p = UserProfile(
            birthYear: 1985,
            biologicalSexRaw: "male",
            heightCM: 175,
            bloodTypeRaw: "A",
            chronicConditions: ["高血压"]
        )
        ctx.insert(p)
        try ctx.save()

        let fetched = try #require(try ctx.fetch(FetchDescriptor<UserProfile>()).first)
        #expect(fetched.birthYear == 1985)
        #expect(fetched.sex == .male)
        #expect(fetched.heightCM == 175)
        #expect(fetched.bloodTypeRaw == "A")
        #expect(fetched.chronicConditions == ["高血压"])
    }

    @Test func medicationRoundtripAndDetailLine() throws {
        let container = try makeContainer()
        let ctx = ModelContext(container)

        let med = Medication(name: "缬沙坦胶囊", strength: "80mg×7粒", usage: "一日一次,一次一粒")
        ctx.insert(med)
        try ctx.save()

        let fetched = try #require(try ctx.fetch(FetchDescriptor<Medication>()).first)
        #expect(fetched.name == "缬沙坦胶囊")
        #expect(fetched.detailLine == "80mg×7粒 · 一日一次,一次一粒")
        #expect(fetched.updatedAt == fetched.createdAt)
    }

    @Test func medicationDetailLineOmitsEmptyParts() {
        #expect(Medication(name: "维生素C").detailLine == "")
        #expect(Medication(name: "钙片", strength: "600mg").detailLine == "600mg")
    }

    @Test func cascadeDeleteMedicationRemovesAssets() throws {
        let container = try makeContainer()
        let ctx = ModelContext(container)

        let med = Medication(name: "二甲双胍缓释片", strength: "0.5g×30片")
        let asset = Asset(relativePath: "med-1.jpg", bytes: 2048)
        ctx.insert(asset)
        med.assets.append(asset)
        ctx.insert(med)
        try ctx.save()
        #expect(med.assets.count == 1)

        ctx.delete(med)
        try ctx.save()

        #expect(try ctx.fetch(FetchDescriptor<Medication>()).isEmpty)
        #expect(try ctx.fetch(FetchDescriptor<Asset>()).isEmpty)
    }
}
