import SwiftUI
import SwiftData

// MARK: - Enums


enum PersonTag: String, CaseIterable, Codable {
    case family    = "Familie"
    case friends   = "Freunde"
    case work      = "Arbeit"
    case community = "Community"
    case other     = "Andere"

    var icon: String {
        switch self {
        case .family:    return "house"
        case .friends:   return "person.2"
        case .work:      return "briefcase"
        case .community: return "person.3"
        case .other:     return "tag"
        }
    }
}

enum NudgeFrequency: String, CaseIterable, Codable {
    case never     = "Nie"
    case weekly    = "Wöchentlich"
    case biweekly  = "2 Wochen"
    case monthly   = "Monatlich"
    case quarterly = "Quartalsweise"

    var days: Int? {
        switch self {
        case .never:     return nil
        case .weekly:    return 7
        case .biweekly:  return 14
        case .monthly:   return 30
        case .quarterly: return 90
        }
    }

    /// Lesbares Label für den Nudge-Chip im Header
    var displayLabel: String {
        switch self {
        case .never:     return "Nie"
        case .weekly:    return "Wöchentlich"
        case .biweekly:  return "Alle 2 Wochen"
        case .monthly:   return "Monatlich"
        case .quarterly: return "Quartalsweise"
        }
    }
}

/// Ampelstatus des Nudge-Intervalls einer Person
enum NudgeStatus: Equatable {
    case never    // kein Intervall gesetzt
    case ok       // < 75 % des Intervalls verstrichen
    case soon     // 75–100 % verstrichen
    case overdue  // > 100 % verstrichen
}

enum MomentType: String, Codable {
    case conversation = "Gespräch"
    case meeting      = "Treffen"   // rawValue bleibt für Persistenz unverändert
    case thought      = "Gedanke"
    case intention    = "Vorhaben"  // legacy — nicht mehr im Picker, aber für Persistenz erhalten

    /// Typen, die im Picker angezeigt werden (.intention ist veraltet und ausgeblendet).
    static var allCases: [MomentType] { [.conversation, .meeting, .thought] }

    /// Anzeigename im UI — entkoppelt Persistenzschlüssel von der Darstellung.
    var displayName: String {
        switch self {
        case .conversation: return "Gespräch/Chat"
        case .intention:    return "Unternehmung"   // legacy
        default:            return rawValue
        }
    }

    var icon: String {
        switch self {
        case .conversation: return "bubble.left"
        case .meeting:      return "person.2"
        case .thought:      return "lightbulb"
        case .intention:    return "arrow.right.circle"
        }
    }
}

enum MomentSource: String, CaseIterable, Codable {
    case whatsapp = "WhatsApp"
    case imessage = "iMessage"
    case telegram = "Telegram"
    case signal   = "Signal"
    case other    = "Chat"

    var icon: String {
        switch self {
        case .whatsapp: return "message.fill"
        case .imessage: return "message.fill"
        case .telegram: return "paperplane.fill"
        case .signal:   return "lock.circle.fill"
        case .other:    return "bubble.left.fill"
        }
    }
}

// MARK: - PersonPhoto
// Ausgelagertes Fotomodell – verhindert, dass Person-Queries Bild-Blobs laden.
// @Attribute(.externalStorage) speichert Bilddaten außerhalb der SQLite-Datenbank
// und nutzt für CloudKit automatisch CKAsset (kein 1MB-Feldlimit).

@Model
final class PersonPhoto {
    var id: UUID = UUID()
    @Attribute(.externalStorage) var imageData: Data = Data()
    var createdAt: Date = Date()

    init(imageData: Data) {
        self.id = UUID()
        self.imageData = imageData
        self.createdAt = Date()
    }
}

// MARK: - Person

@Model
class Person {
    var id: UUID = UUID()
    var name: String = ""
    var tagRaw: String = PersonTag.other.rawValue
    var birthday: Date?
    var occupation: String?
    var location: String?
    var interests: String?
    var generalNotes: String?
    var culturalBackground: String? = nil  // V6: kultureller Hintergrund
    var phoneNumber: String? = nil         // V9: primäre Telefonnummer
    var emailAddress: String? = nil        // V9: primäre E-Mail-Adresse
    var cnIdentifier: String? = nil        // V9: Apple Contacts-ID für "Vom Kontakt aktualisieren"
    var nudgeFrequencyRaw: String = NudgeFrequency.monthly.rawValue
    var nextStep: String?
    var nextStepCompleted: Bool = false
    var nextStepReminderDate: Date?
    var lastSuggestedForCall: Date?
    var createdAt: Date = Date()
    var updatedAt: Date = Date()  // V3: für Sync-Konflikt-Erkennung
    var isArchived: Bool = false   // V3: soft delete – Daten bleiben erhalten

    // V3: Foto als eigenes Modell (lazy geladen – kein Blob beim Listen-Fetch)
    @Relationship(deleteRule: .cascade) var photo: PersonPhoto? = nil

    // Übergangsfeld: vorhanden bis Repair-Pass photo befüllt hat.
    // Wird nach Migration auf nil gesetzt. In V4 entfernbar.
    var photoData: Data? = nil

    @Relationship(deleteRule: .cascade) var moments: [Moment]? = []
    @Relationship(deleteRule: .cascade) var logEntries: [LogEntry]? = []
    @Relationship(deleteRule: .cascade) var visits: [Visit]? = []  // V4
    @Relationship(deleteRule: .cascade) var todos: [Todo]? = []    // V7

    init(
        name: String,
        tag: PersonTag = .other,
        birthday: Date? = nil,
        occupation: String? = nil,
        location: String? = nil,
        interests: String? = nil,
        generalNotes: String? = nil,
        culturalBackground: String? = nil,
        nudgeFrequency: NudgeFrequency = .monthly
    ) {
        self.id = UUID()
        self.name = name
        self.tagRaw = tag.rawValue
        self.birthday = birthday
        self.occupation = occupation
        self.location = location
        self.interests = interests
        self.generalNotes = generalNotes
        self.culturalBackground = culturalBackground
        self.phoneNumber = nil
        self.emailAddress = nil
        self.cnIdentifier = nil
        self.nudgeFrequencyRaw = nudgeFrequency.rawValue
        self.photoData = nil
        self.photo = nil
        self.nextStep = nil
        self.nextStepCompleted = false
        self.nextStepReminderDate = nil
        self.lastSuggestedForCall = nil
        self.createdAt = Date()
        self.updatedAt = Date()
        self.isArchived = false
        self.moments = []
        self.logEntries = []
        self.visits = []
        self.todos = []
    }

    var tag: PersonTag {
        get { PersonTag(rawValue: tagRaw) ?? .other }
        set { tagRaw = newValue.rawValue }
    }

    var nudgeFrequency: NudgeFrequency {
        get { NudgeFrequency(rawValue: nudgeFrequencyRaw) ?? .monthly }
        set { nudgeFrequencyRaw = newValue.rawValue }
    }

    var lastMomentDate: Date? {
        (moments ?? []).sorted { $0.createdAt > $1.createdAt }.first?.createdAt
    }

    var needsAttention: Bool {
        guard let days = nudgeFrequency.days else { return false }
        if let last = lastMomentDate {
            return Date().timeIntervalSince(last) > Double(days * 86400)
        }
        return Date().timeIntervalSince(createdAt) > Double(days * 86400)
    }

    /// Dreistufiger Ampelstatus basierend auf verstrichener Zeit vs. Nudge-Intervall
    var nudgeStatus: NudgeStatus {
        guard nudgeFrequency != .never, let days = nudgeFrequency.days else { return .never }
        let interval = Double(days * 86400)
        let reference = lastMomentDate ?? createdAt
        let elapsed = Date().timeIntervalSince(reference)
        if elapsed >= interval         { return .overdue }
        if elapsed >= interval * 0.75  { return .soon }
        return .ok
    }

    func hasBirthdayWithin(days: Int) -> Bool {
        guard let birthday else { return false }
        let cal = Calendar.current
        let bdc = cal.dateComponents([.month, .day], from: birthday)
        guard let bMonth = bdc.month, let bDay = bdc.day else { return false }
        for offset in 0..<days {
            if let date = cal.date(byAdding: .day, value: offset, to: Date()) {
                let dc = cal.dateComponents([.month, .day], from: date)
                if dc.month == bMonth && dc.day == bDay { return true }
            }
        }
        return false
    }

    var initials: String {
        let parts = name.split(separator: " ")
        if parts.count >= 2 {
            return (parts[0].prefix(1) + parts[1].prefix(1)).uppercased()
        }
        return String(name.prefix(2)).uppercased()
    }

    var firstName: String {
        String(name.split(separator: " ").first ?? Substring(name))
    }

    var sortedMoments: [Moment] {
        (moments ?? []).sorted { $0.createdAt > $1.createdAt }
    }

    var sortedLogEntries: [LogEntry] {
        (logEntries ?? []).sorted { $0.loggedAt > $1.loggedAt }
    }

    /// Einheitlicher Foto-Zugriff: bevorzugt das migrierte PersonPhoto,
    /// fällt auf legacyPhotoData zurück bis der Repair-Pass gelaufen ist.
    var currentPhotoData: Data? {
        photo?.imageData ?? photoData
    }

    var sortedVisits: [Visit] {
        (visits ?? []).sorted { $0.visitDate > $1.visitDate }
    }

    var lastVisit: Visit? {
        sortedVisits.first
    }

    var visitCount: Int {
        (visits ?? []).count
    }

    // MARK: Meeting-Momente (V5)

    var meetingMoments: [Moment] {
        (moments ?? []).filter { $0.type == .meeting }
    }

    var sortedMeetings: [Moment] {
        meetingMoments.sorted { $0.createdAt > $1.createdAt }
    }

    var lastMeeting: Moment? {
        sortedMeetings.first
    }

    var lastMeetingDate: Date? {
        lastMeeting?.createdAt
    }

    var meetingCount: Int {
        meetingMoments.count
    }

    // MARK: Offene Vorhaben (V5)

    /// Alle Vorhaben-Momente, die noch nicht abgehakt wurden.
    var openIntentions: [Moment] {
        (moments ?? []).filter { $0.isOpen }
            .sorted { $0.createdAt < $1.createdAt }
    }

    var openTodos: [Todo] {
        (todos ?? []).filter { !$0.isCompleted }
            .sorted { $0.dueDate < $1.dueDate }
    }

    var sortedTodos: [Todo] {
        (todos ?? []).sorted { $0.dueDate < $1.dueDate }
    }

    /// Muss nach jeder inhaltlichen Änderung aufgerufen werden.
    func touch() {
        updatedAt = Date()
    }
}

// MARK: - LogEntryType

enum LogEntryType: String, Codable, CaseIterable {
    case nextStep      = "Schritt abgeschlossen"
    case calendarEvent = "Termin geplant"
    case call          = "Anruf"
    case todoCompleted = "Todo abgeschlossen"

    var icon: String {
        switch self {
        case .nextStep:      return "checkmark.circle.fill"
        case .calendarEvent: return "calendar.badge.checkmark"
        case .call:          return "phone.circle.fill"
        case .todoCompleted: return "checkmark.square.fill"
        }
    }

    var color: String {
        switch self {
        case .nextStep:      return "green"
        case .calendarEvent: return "blue"
        case .call:          return "accent"
        case .todoCompleted: return "green"
        }
    }
}

// MARK: - LogEntry

@Model
class LogEntry {
    var id: UUID = UUID()
    var typeRaw: String = LogEntryType.nextStep.rawValue
    var title: String = ""
    var loggedAt: Date = Date()
    var updatedAt: Date = Date()  // V3
    var person: Person?

    init(type: LogEntryType, title: String, person: Person? = nil) {
        self.id = UUID()
        self.typeRaw = type.rawValue
        self.title = title
        self.loggedAt = Date()
        self.updatedAt = Date()
        self.person = person
    }

    var type: LogEntryType {
        get { LogEntryType(rawValue: typeRaw) ?? .nextStep }
        set { typeRaw = newValue.rawValue }
    }
}

// MARK: - Moment

@Model
class Moment {
    var id: UUID = UUID()
    var text: String = ""
    var typeRaw: String = MomentType.conversation.rawValue
    var sourceRaw: String? = nil
    var createdAt: Date = Date()
    var updatedAt: Date = Date()  // V3
    var isImportant: Bool = false  // Vom Nutzer als wichtig markiert
    var person: Person?

    // V5: Meeting-Bewertungsfelder (ehemals auf Visit)
    @Relationship(deleteRule: .cascade) var ratings: [Rating]? = []
    @Relationship(deleteRule: .cascade) var healthSnapshot: HealthSnapshot? = nil
    var statusRaw: String? = nil          // nil für Nicht-Treffen; VisitStatus-rawValue für Treffen
    var aftermathNotificationScheduled: Bool = false
    var aftermathCompletedAt: Date? = nil

    // V5: Vorhaben-Felder (ehemals Person.nextStep / nextStepReminderDate)
    var reminderDate: Date? = nil         // Erinnerungstermin für Vorhaben
    var isCompleted: Bool = false         // Ob das Vorhaben abgehakt wurde

    init(text: String, type: MomentType = .conversation, source: MomentSource? = nil, person: Person? = nil) {
        self.id = UUID()
        self.text = text
        self.typeRaw = type.rawValue
        self.sourceRaw = source?.rawValue
        self.createdAt = Date()
        self.updatedAt = Date()
        self.isImportant = false
        self.person = person
        self.ratings = []
        self.healthSnapshot = nil
        self.statusRaw = nil
        self.aftermathNotificationScheduled = false
        self.aftermathCompletedAt = nil
        self.reminderDate = nil
        self.isCompleted = false
    }

    var type: MomentType {
        get { MomentType(rawValue: typeRaw) ?? .conversation }
        set { typeRaw = newValue.rawValue }
    }

    var source: MomentSource? {
        get { sourceRaw.flatMap { MomentSource(rawValue: $0) } }
        set { sourceRaw = newValue?.rawValue }
    }

    // MARK: Meeting-Hilfseigenschaften (V5)

    var isMeeting: Bool { type == .meeting }

    var meetingStatus: VisitStatus? {
        get { statusRaw.flatMap { VisitStatus(rawValue: $0) } }
        set { statusRaw = newValue?.rawValue }
    }

    var isMeetingComplete: Bool {
        meetingStatus == .completed
    }

    var sortedRatings: [Rating] {
        (ratings ?? []).sorted { $0.questionIndex < $1.questionIndex }
    }

    func averageForCategory(_ category: RatingCategory, aftermath: Bool) -> Double? {
        let filtered = (ratings ?? []).filter {
            $0.category == category && $0.isAftermath == aftermath
        }
        let valued = filtered.compactMap { $0.value }
        guard !valued.isEmpty else { return nil }
        return Double(valued.reduce(0, +)) / Double(valued.count)
    }

    var immediateAverage: Double? {
        let values = (ratings ?? []).filter { !$0.isAftermath }.compactMap { $0.value }
        guard !values.isEmpty else { return nil }
        return Double(values.reduce(0, +)) / Double(values.count)
    }

    var aftermathAverage: Double? {
        let values = (ratings ?? []).filter { $0.isAftermath }.compactMap { $0.value }
        guard !values.isEmpty else { return nil }
        return Double(values.reduce(0, +)) / Double(values.count)
    }

    // MARK: Vorhaben-Hilfseigenschaften (V5)

    var isIntention: Bool { type == .intention }

    /// Ein Vorhaben das noch nicht abgehakt wurde.
    var isOpen: Bool { isIntention && !isCompleted }
}

// MARK: - Visit Rating Enums

enum RatingCategory: String, CaseIterable, Codable {
    case selbst      = "Selbst"
    case beziehung   = "Beziehung"
    case gespraech   = "Gespräch"
    case nachwirkung = "Nachwirkung"

    var icon: String {
        switch self {
        case .selbst:      return "person.fill"
        case .beziehung:   return "heart.fill"
        case .gespraech:   return "bubble.left.fill"
        case .nachwirkung: return "moon.stars.fill"
        }
    }

    var color: Color {
        switch self {
        case .selbst:      return .blue
        case .beziehung:   return .pink
        case .gespraech:   return .orange
        case .nachwirkung: return .purple
        }
    }
}

enum VisitStatus: String, Codable {
    case immediateCompleted = "sofort_abgeschlossen"
    case awaitingAftermath  = "warte_nachwirkung"
    case completed          = "abgeschlossen"
}

// MARK: - RatingQuestion

struct RatingQuestion {
    let category: RatingCategory
    let text: String
    let negativePole: String
    let positivePole: String
    let isAftermath: Bool

    static let all: [RatingQuestion] = [
        // MARK: Selbst (2 Fragen)
        RatingQuestion(category: .selbst,
                       text: "Wie hast du dich während des Treffens gefühlt?",
                       negativePole: "Unwohl",
                       positivePole: "Sehr wohl",
                       isAftermath: false),
        RatingQuestion(category: .selbst,
                       text: "Wie ist dein Energielevel nach dem Treffen?",
                       negativePole: "Erschöpft",
                       positivePole: "Energiegeladen",
                       isAftermath: false),
        // MARK: Beziehung (2 Fragen)
        RatingQuestion(category: .beziehung,
                       text: "Fühlt sich die Beziehung gestärkt an?",
                       negativePole: "Distanzierter",
                       positivePole: "Viel näher",
                       isAftermath: false),
        RatingQuestion(category: .beziehung,
                       text: "War das Treffen ausgeglichen (Geben/Nehmen)?",
                       negativePole: "Sehr einseitig",
                       positivePole: "Perfekt ausgeglichen",
                       isAftermath: false),
        // MARK: Gespräch (1 Frage)
        RatingQuestion(category: .gespraech,
                       text: "Wie tiefgehend waren die Gespräche?",
                       negativePole: "Nur Smalltalk",
                       positivePole: "Sehr tiefgründig",
                       isAftermath: false),
        // MARK: Nachwirkung (4 Fragen – isAftermath = true)
        RatingQuestion(category: .nachwirkung,
                       text: "Möchtest du die Person bald wiedersehen?",
                       negativePole: "Eher nicht",
                       positivePole: "Unbedingt",
                       isAftermath: true),
        RatingQuestion(category: .nachwirkung,
                       text: "Wie denkst du jetzt über das Treffen?",
                       negativePole: "Eher negativ",
                       positivePole: "Sehr positiv",
                       isAftermath: true),
        RatingQuestion(category: .nachwirkung,
                       text: "Hat sich deine Sicht auf die Person verändert?",
                       negativePole: "Zum Schlechteren",
                       positivePole: "Zum Besseren",
                       isAftermath: true),
        RatingQuestion(category: .nachwirkung,
                       text: "Würdest du ein ähnliches Treffen wiederholen?",
                       negativePole: "Eher nicht",
                       positivePole: "Sofort wieder",
                       isAftermath: true),
    ]

    static var immediate: [RatingQuestion] { all.filter { !$0.isAftermath } }
    static var aftermath: [RatingQuestion]  { all.filter {  $0.isAftermath } }
}

// MARK: - Visit

@Model
class Visit {
    var id: UUID = UUID()
    var visitDate: Date = Date()
    var statusRaw: String = VisitStatus.immediateCompleted.rawValue
    var note: String? = nil
    var aftermathNotificationScheduled: Bool = false
    var aftermathCompletedAt: Date? = nil

    var person: Person? = nil
    @Relationship(deleteRule: .cascade) var ratings: [Rating]? = []
    @Relationship(deleteRule: .cascade) var healthSnapshot: HealthSnapshot? = nil

    init(visitDate: Date = Date(), person: Person? = nil) {
        self.id = UUID()
        self.visitDate = visitDate
        self.statusRaw = VisitStatus.immediateCompleted.rawValue
        self.note = nil
        self.aftermathNotificationScheduled = false
        self.aftermathCompletedAt = nil
        self.person = person
        self.ratings = []
        self.healthSnapshot = nil
    }

    var status: VisitStatus {
        get { VisitStatus(rawValue: statusRaw) ?? .immediateCompleted }
        set { statusRaw = newValue.rawValue }
    }

    var isComplete: Bool {
        status == .completed
    }

    var sortedRatings: [Rating] {
        (ratings ?? []).sorted { $0.questionIndex < $1.questionIndex }
    }

    func averageForCategory(_ category: RatingCategory, aftermath: Bool) -> Double? {
        let filtered = (ratings ?? []).filter {
            $0.category == category && $0.isAftermath == aftermath
        }
        let valued = filtered.compactMap { $0.value }
        guard !valued.isEmpty else { return nil }
        return Double(valued.reduce(0, +)) / Double(valued.count)
    }

    var immediateAverage: Double? {
        let values = (ratings ?? [])
            .filter { !$0.isAftermath }
            .compactMap { $0.value }
        guard !values.isEmpty else { return nil }
        return Double(values.reduce(0, +)) / Double(values.count)
    }

    var aftermathAverage: Double? {
        let values = (ratings ?? [])
            .filter { $0.isAftermath }
            .compactMap { $0.value }
        guard !values.isEmpty else { return nil }
        return Double(values.reduce(0, +)) / Double(values.count)
    }
}

// MARK: - Rating

@Model
class Rating {
    var id: UUID = UUID()
    var categoryRaw: String = RatingCategory.selbst.rawValue
    var questionIndex: Int = 0
    var value: Int? = nil       // nil = übersprungen; -2...+2 sonst
    var isAftermath: Bool = false
    var visit: Visit? = nil     // V4 legacy – nach Migration immer nil
    var moment: Moment? = nil   // V5 – primäre Referenz

    init(category: RatingCategory, questionIndex: Int, value: Int?, isAftermath: Bool,
         visit: Visit? = nil, moment: Moment? = nil) {
        self.id = UUID()
        self.categoryRaw = category.rawValue
        self.questionIndex = questionIndex
        self.value = value
        self.isAftermath = isAftermath
        self.visit = visit
        self.moment = moment
    }

    var category: RatingCategory {
        get { RatingCategory(rawValue: categoryRaw) ?? .selbst }
        set { categoryRaw = newValue.rawValue }
    }
}

// MARK: - Todo (V7)
// Aufgaben mit Fälligkeitsdatum, die einer Person zugeordnet sind.
// Im Gegensatz zu Momenten sind Todos rein aufgabenorientiert.

@Model
class Todo {
    var id: UUID = UUID()
    var title: String = ""
    var dueDate: Date = Date()
    var isCompleted: Bool = false
    var completedAt: Date? = nil
    var reminderDate: Date? = nil  // V8: optionale Push-Benachrichtigung
    var person: Person?
    var createdAt: Date = Date()

    init(title: String, dueDate: Date, reminderDate: Date? = nil, person: Person? = nil) {
        self.id = UUID()
        self.title = title
        self.dueDate = dueDate
        self.isCompleted = false
        self.completedAt = nil
        self.reminderDate = reminderDate
        self.person = person
        self.createdAt = Date()
    }
}

// MARK: - HealthSnapshot (Phase-2-Platzhalter)

@Model
class HealthSnapshot {
    var id: UUID = UUID()
    var sleepHours: Double? = nil
    var hrvMs: Double? = nil
    var restingHR: Int? = nil
    var steps: Int? = nil
    var visit: Visit? = nil     // V4 legacy – nach Migration immer nil
    var moment: Moment? = nil   // V5 – primäre Referenz

    init(visit: Visit? = nil, moment: Moment? = nil) {
        self.id = UUID()
        self.visit = visit
        self.moment = moment
    }

    var hasData: Bool {
        sleepHours != nil || hrvMs != nil || restingHR != nil || steps != nil
    }
}
