import SwiftUI
import SwiftData
import OSLog

private let logger = Logger(subsystem: "nahbar", category: "ContentView")

struct ContentView: View {
    /// Wird von NahbarApp auf `true` gesetzt, solange der Splash-Screen sichtbar ist.
    /// UI-Präsentation (Sheets, Onboarding) startet erst, wenn dieser Wert auf `false` wechselt.
    var splashVisible: Bool = false

    @AppStorage("nahbarOnboardingDone")     private var nahbarOnboardingDone = false
    @AppStorage("callWindowOnboardingDone") private var onboardingDone = false
    @AppStorage("callSuggestionDate")       private var suggestionDateStr = ""
    @AppStorage("photoRepairPassDone")      private var photoRepairPassDone = false
    @AppStorage("visitMigrationPassDone")   private var visitMigrationPassDone = false
    @AppStorage("nextStepMigrationPassDone") private var nextStepMigrationPassDone = false

    @EnvironmentObject private var callWindowManager: CallWindowManager
    @EnvironmentObject private var appLockManager: AppLockManager
    @EnvironmentObject private var cloudSyncMonitor: CloudSyncMonitor
    @Environment(\.scenePhase)   private var scenePhase
    @Environment(\.modelContext) private var modelContext
    @Environment(\.nahbarTheme)  private var theme

    @Environment(TourCoordinator.self) private var tourCoordinator

    @Query private var persons: [Person]

    @State private var showingNahbarOnboarding = false
    @State private var showingOnboarding  = false
    @State private var suggestedPerson: Person? = nil
    @State private var showingSuggestion  = false
    /// Steuert den aktiven Tab; nötig damit die Tour auf dem Menschen-Tab starten kann.
    @State private var selectedTab: Int = 0

    var body: some View {
        TabView(selection: $selectedTab) {
            TodayView()
                .tabItem { Label("Heute", systemImage: "sun.max") }
                .tag(0)

            PeopleListView()
                .tabItem { Label("Menschen", systemImage: "person.2") }
                .tag(1)

            IchView()
                .tabItem { Label("Ich", systemImage: "person.circle") }
                .tag(2)

            SettingsView()
                .tabItem { Label("Einstellungen", systemImage: "gearshape") }
                .tag(3)
        }
        // Tab-Bar-Styling einmalig auf TabView statt auf jedem Tab setzen –
        // verhindert weißes Aufblitzen beim Tab-Wechsel (Issue #36)
        .toolbarBackground(theme.backgroundPrimary.opacity(0.92), for: .tabBar)
        .toolbarBackground(.visible, for: .tabBar)
        .toolbarColorScheme(theme.id.isDark ? .dark : .light, for: .tabBar)
        .fullScreenCover(isPresented: $showingNahbarOnboarding) {
            OnboardingContainerView {
                nahbarOnboardingDone = true
                showingNahbarOnboarding = false
                checkCallWindow()
                scheduleTourIfNeeded()
            }
        }
        .sheet(isPresented: $showingOnboarding) {
            CallWindowSetupView(
                manager: callWindowManager,
                isOnboarding: true,
                onDone: {
                    showingOnboarding = false
                    onboardingDone = true
                }
            )
            .presentationDetents([.large])
        }
        .sheet(isPresented: $showingSuggestion) {
            if let person = suggestedPerson {
                CallSuggestionView(person: person) {
                    person.lastSuggestedForCall = Date()
                    person.touch()
                    suggestionDateStr = ISO8601DateFormatter().string(from: Date())
                    let entry = LogEntry(type: .call, title: String.localizedStringWithFormat(String(localized: "Anruf mit %@"), person.firstName), person: person)
                    modelContext.insert(entry)
                    person.logEntries?.append(entry)
                    save()
                }
            }
        }
        .tourPresenter(coordinator: tourCoordinator)
        .onAppear {
            // Datenoperationen sofort starten (unabhängig vom Splash-Screen)
            syncPeopleCache()
            importPendingMoments()
            runPhotoRepairPass()
            runVisitMigrationPass()
            runNextStepMigrationPass()
            // UI-Präsentation erst nach dem Splash
            if !splashVisible {
                showPendingUI()
            }
        }
        .onChange(of: splashVisible) { _, visible in
            // Sobald der Splash verschwindet, UI-Logik starten
            if !visible {
                showPendingUI()
            }
        }
        .onChange(of: scenePhase) { _, phase in
            if phase == .active {
                syncPeopleCache()
                importPendingMoments()
                if !splashVisible { checkCallWindow() }
            }
        }
        .onChange(of: persons) { _, _ in
            syncPeopleCache()
        }
    }

    // MARK: - Pending UI

    /// Startet die UI-Präsentation nach dem Splash-Screen:
    /// Onboarding, Call-Window-Setup oder Gesprächsvorschlag.
    private func showPendingUI() {
        if !nahbarOnboardingDone {
            showingNahbarOnboarding = true
        } else if !onboardingDone {
            showingOnboarding = true
        } else {
            checkCallWindow()
            tourCoordinator.checkForPendingTours()
            scheduleTourIfNeeded()
        }
    }

    // MARK: - Tour

    /// Wechselt zum Menschen-Tab (damit PeopleListView gerendert wird und seine
    /// anchorPreference-Frames sammeln kann), wartet 700 ms und startet dann die Tour.
    /// Die Verzögerung deckt die fullScreenCover-Dismiss-Animation (~350 ms) ab.
    private func scheduleTourIfNeeded() {
        guard !tourCoordinator.hasSeenOnboardingTour else { return }
        selectedTab = 1
        Task { @MainActor in
            try? await Task.sleep(for: .milliseconds(700))
            tourCoordinator.startOnboardingTourIfNeeded()
        }
    }

    // MARK: - Call Window

    private func checkCallWindow() {
        guard callWindowManager.isEnabled,
              callWindowManager.isCurrentlyInWindow,
              !suggestionShownToday,
              !showingSuggestion,
              !appLockManager.isLocked else { return }

        if let person = callWindowManager.selectPerson(from: persons) {
            suggestedPerson = person
            showingSuggestion = true
        }
    }

    // MARK: - App Group Sync

    private func syncPeopleCache() {
        AppGroup.savePeopleList(persons)
    }

    // MARK: - Import Pending Moments

    /// Importiert Momente aus der Share Extension.
    /// Primärer Identifier ist die personID (UUID), Name dient als Fallback.
    private func importPendingMoments() {
        let pending = AppGroup.pendingMoments
        guard !pending.isEmpty else { return }

        var importedCount = 0
        for entry in pending {
            guard let text    = entry["text"],
                  let typeRaw = entry["type"] else {
                logger.warning("Ungültiger pendingMoment-Eintrag – übersprungen: \(entry)")
                continue
            }

            // UUID-Matching (robust) mit Name-Fallback (kompatibel mit alten Einträgen)
            let person: Person?
            if let idString = entry["personID"],
               let uuid = UUID(uuidString: idString) {
                person = persons.first { $0.id == uuid }
                    ?? persons.first { $0.name == entry["personName"] }
            } else {
                person = persons.first { $0.name == entry["personName"] }
            }

            guard let person else {
                logger.warning("Person für Moment nicht gefunden: \(entry["personName"] ?? "?")")
                continue
            }

            let type   = MomentType(rawValue: typeRaw) ?? .conversation
            let source = entry["source"].flatMap { MomentSource(rawValue: $0) }
            let moment = Moment(text: text, type: type, source: source, person: person)
            modelContext.insert(moment)
            person.moments?.append(moment)
            person.touch()
            importedCount += 1
        }

        if importedCount > 0 {
            save()
            logger.info("\(importedCount) Momente aus Share Extension importiert")
            AppEventLog.shared.record("\(importedCount) Moment(e) aus Share Extension importiert", level: .success, category: "Import")
        }
        AppGroup.clearPendingMoments()
    }

    // MARK: - Photo Repair Pass (V2 → V3 Datenmigration)

    /// Überführt photoData (legacy Blob direkt auf Person) in PersonPhoto-Objekte.
    /// Läuft einmalig nach der Schema-V3-Migration. Danach: photoRepairPassDone = true.
    private func runPhotoRepairPass() {
        guard !photoRepairPassDone else { return }

        let descriptor = FetchDescriptor<Person>(
            predicate: #Predicate<Person> { person in
                person.photoData != nil && person.photo == nil
            }
        )

        guard let personsNeedingRepair = try? modelContext.fetch(descriptor),
              !personsNeedingRepair.isEmpty else {
            photoRepairPassDone = true
            logger.info("Photo Repair Pass: nichts zu migrieren")
            return
        }

        logger.info("Photo Repair Pass: \(personsNeedingRepair.count) Person(en) werden migriert")
        for person in personsNeedingRepair {
            guard let data = person.photoData else { continue }
            let photo = PersonPhoto(imageData: data)
            modelContext.insert(photo)
            person.photo    = photo
            person.photoData = nil
            person.touch()
        }

        save()
        photoRepairPassDone = true
        logger.info("Photo Repair Pass abgeschlossen")
        AppEventLog.shared.record("Foto-Migration: \(personsNeedingRepair.count) Person(en) migriert", level: .success, category: "Migration")
    }

    // MARK: - Visit Migration Pass (V4 → V5 Datenmigration)

    /// Überführt Visit-Objekte in Meeting-Momente.
    /// Ratings und HealthSnapshot werden auf den neuen Moment umgehängt, BEVOR das Visit
    /// gelöscht wird – sonst würde die Cascade-Delete-Rule die Ratings mitlöschen.
    /// Läuft einmalig nach der Schema-V5-Migration. Danach: visitMigrationPassDone = true.
    private func runVisitMigrationPass() {
        guard !visitMigrationPassDone else { return }

        let descriptor = FetchDescriptor<Visit>()
        guard let visits = try? modelContext.fetch(descriptor) else {
            visitMigrationPassDone = true
            logger.info("Visit Migration Pass: Fetch fehlgeschlagen")
            return
        }

        guard !visits.isEmpty else {
            visitMigrationPassDone = true
            logger.info("Visit Migration Pass: nichts zu migrieren")
            return
        }

        logger.info("Visit Migration Pass: \(visits.count) Visit(s) werden migriert")
        for visit in visits {
            let moment = Moment(
                text: visit.note ?? "",
                type: .meeting,
                source: nil,
                person: visit.person
            )
            moment.createdAt = visit.visitDate
            moment.updatedAt = visit.visitDate
            moment.statusRaw = visit.statusRaw
            moment.aftermathNotificationScheduled = visit.aftermathNotificationScheduled
            moment.aftermathCompletedAt = visit.aftermathCompletedAt
            modelContext.insert(moment)
            visit.person?.moments?.append(moment)

            // Ratings umhängen BEVOR Visit gelöscht wird (Cascade-Delete-Schutz)
            for rating in (visit.ratings ?? []) {
                rating.visit = nil
                rating.moment = moment
            }

            // HealthSnapshot umhängen
            if let snapshot = visit.healthSnapshot {
                snapshot.visit = nil
                snapshot.moment = moment
            }

            modelContext.delete(visit)
        }

        save()
        visitMigrationPassDone = true
        logger.info("Visit Migration Pass abgeschlossen")
        AppEventLog.shared.record(
            "Treffen-Migration: \(visits.count) Visit(s) zu Momenten konvertiert",
            level: .success,
            category: "Migration"
        )
    }

    // MARK: - Next Step Migration Pass (V4 → V5 Datenmigration)

    /// Überführt Person.nextStep-Felder in Vorhaben-Momente.
    /// Läuft einmalig nach der Schema-V5-Migration. Danach: nextStepMigrationPassDone = true.
    private func runNextStepMigrationPass() {
        guard !nextStepMigrationPassDone else { return }

        let personsWithNextStep = persons.filter {
            let step = $0.nextStep
            return step != nil && !(step!.isEmpty)
        }

        guard !personsWithNextStep.isEmpty else {
            nextStepMigrationPassDone = true
            logger.info("Next Step Migration Pass: nichts zu migrieren")
            return
        }

        logger.info("Next Step Migration Pass: \(personsWithNextStep.count) Person(en) werden migriert")
        for person in personsWithNextStep {
            guard let text = person.nextStep, !text.isEmpty else { continue }

            let moment = Moment(text: text, type: .intention, source: nil, person: person)
            moment.reminderDate = person.nextStepReminderDate
            moment.isCompleted = person.nextStepCompleted
            modelContext.insert(moment)
            person.moments?.append(moment)

            // Legacy-Felder leeren
            person.nextStep = nil
            person.nextStepCompleted = false
            person.nextStepReminderDate = nil
            person.touch()
        }

        save()
        nextStepMigrationPassDone = true
        logger.info("Next Step Migration Pass abgeschlossen")
        AppEventLog.shared.record(
            "Vorhaben-Migration: \(personsWithNextStep.count) Nächste-Schritte zu Momenten konvertiert",
            level: .success,
            category: "Migration"
        )
    }

    // MARK: - Helpers

    private func save() {
        do {
            try modelContext.save()
        } catch {
            logger.error("modelContext.save() fehlgeschlagen: \(error.localizedDescription)")
            AppEventLog.shared.record("Speichern fehlgeschlagen: \(error.localizedDescription)", level: .error, category: "Store")
        }
    }

    private var suggestionShownToday: Bool {
        guard !suggestionDateStr.isEmpty,
              let date = ISO8601DateFormatter().date(from: suggestionDateStr) else { return false }
        return Calendar.current.isDateInToday(date)
    }
}

#Preview {
    ContentView()
        .modelContainer(for: [Person.self, Moment.self, PersonPhoto.self], inMemory: true)
        .environmentObject(CallWindowManager.shared)
        .environmentObject(AppLockManager.shared)
        .environmentObject(CloudSyncMonitor())
        .environmentObject(UserProfileStore.shared)
        .environment(TourCoordinator())
}
