import ForgejoKit
import SwiftData
import SwiftUI

enum AppAppearance: Int, CaseIterable {
    case system = 0
    case light = 1
    case dark = 2

    var colorScheme: ColorScheme? {
        switch self {
        case .system: nil
        case .light: .light
        case .dark: .dark
        }
    }
}

struct ContentView: View {
    @AppStorage("appearance") private var appearance: AppAppearance = .system
    @AppStorage("defaultAllInstances") private var defaultAllInstances = false
    @State var authService: AuthenticationService
    @Binding var multiInstanceManager: MultiInstanceManager?
    @Query(filter: #Predicate<ForgejoInstance> { $0.isDefault }) private var defaultInstances: [ForgejoInstance]
    @Query private var allInstances: [ForgejoInstance]
    @State private var hasAttemptedAutoLogin = false
    @State private var sessionFullyRestored = false
    @Environment(\.modelContext) private var modelContext
    @Environment(NavigationState.self) private var navigationState

    // Dev auto-login credentials, set via launch arguments for development and UI integration tests
    #if DEBUG
        @AppStorage("dev_serverURL") private var devServerURL = ""
        @AppStorage("dev_username") private var devUsername = ""
        @AppStorage("dev_password") private var devPassword = ""
        @AppStorage("dev_skipAutoLogin") private var devSkipAutoLogin = false
        @AppStorage("dev_resetData") private var devResetData = false
    #endif

    var body: some View {
        Group {
            if let manager = multiInstanceManager {
                HomeView(multiInstanceManager: manager) {
                    multiInstanceManager?.disconnect()
                    multiInstanceManager = nil
                }
                .task { await completeMultiInstanceRestore(manager: manager) }
            } else if authService.isAuthenticated, authService.client != nil {
                HomeView(authService: authService)
                    .task { await completeSessionRestore() }
            } else if !hasAttemptedAutoLogin {
                ProgressView("Connecting...")
                    .task { await attemptAutoLogin() }
            } else {
                InstanceListView(authService: authService, multiInstanceManager: $multiInstanceManager)
            }
        }
        .preferredColorScheme(appearance.colorScheme)
        .onChange(of: navigationState.pendingNavigation) { _, pending in
            guard let pending else { return }
            guard authService.isAuthenticated,
                  let currentInstance = authService.currentInstance
            else { return }

            let currentURL = ForgejoClient.normalizeServerURL(currentInstance.serverURL)
            let pendingURL = ForgejoClient.normalizeServerURL(pending.serverURL)
            if currentURL != pendingURL || currentInstance.username != pending.username {
                Task { await switchInstance(to: pending) }
            }
        }
    }

    private func completeMultiInstanceRestore(manager: MultiInstanceManager) async {
        guard !sessionFullyRestored else { return }
        await manager.connect(instances: allInstances)
        sessionFullyRestored = true
    }

    private func completeSessionRestore() async {
        guard !sessionFullyRestored, let instance = authService.currentInstance else { return }
        do {
            try await authService.restoreSession(instance: instance)
            instance.lastUsed = Date()
            try? modelContext.save()
        } catch {
            await authService.logout(modelContext: modelContext)
        }
        sessionFullyRestored = true
    }

    private func attemptAutoLogin() async {
        #if DEBUG
            if devResetData {
                do {
                    try modelContext.delete(model: ForgejoInstance.self)
                    try modelContext.save()
                } catch {
                    assertionFailure("dev_resetData failed: \(error)")
                }
                DiskCache.removeAll()
                devResetData = false
            }
            if devSkipAutoLogin {
                hasAttemptedAutoLogin = true
                return
            }
            if await attemptDevAutoLogin() {
                hasAttemptedAutoLogin = true
                return
            }
        #endif

        if defaultAllInstances, allInstances.count > 1 {
            await attemptMultiInstanceLogin()
        } else {
            await attemptSingleInstanceLogin()
        }
        hasAttemptedAutoLogin = true
    }

    private func attemptMultiInstanceLogin() async {
        if multiInstanceManager != nil {
            // Already bootstrapped synchronously in ForjiApp.init
            return
        }
        let manager = MultiInstanceManager()
        await manager.connect(instances: allInstances)
        if manager.isConnected {
            multiInstanceManager = manager
        }
    }

    private func attemptSingleInstanceLogin() async {
        guard let defaultInstance = defaultInstances.first else { return }

        if authService.bootstrapSession(instance: defaultInstance) {
            do {
                try await authService.restoreSession(instance: defaultInstance)
                defaultInstance.lastUsed = Date()
                try? modelContext.save()
                sessionFullyRestored = true
            } catch {
                await authService.logout(modelContext: modelContext)
            }
            return
        }

        do {
            try await authService.restoreSession(instance: defaultInstance)
            defaultInstance.lastUsed = Date()
            try? modelContext.save()
        } catch {
            // Auto-login failed, fall through to instance list
        }
    }

    #if DEBUG
        private func attemptDevAutoLogin() async -> Bool {
            guard !devServerURL.isEmpty, !devUsername.isEmpty, !devPassword.isEmpty else {
                return false
            }
            do {
                try await authService.login(
                    serverURL: devServerURL, username: devUsername, password: devPassword,
                )
                let descriptor = FetchDescriptor<ForgejoInstance>(predicate: #Predicate {
                    $0.serverURL == devServerURL && $0.username == devUsername
                })
                let existing = (try? modelContext.fetch(descriptor)) ?? []
                let instance: ForgejoInstance
                if let found = existing.first {
                    instance = found
                } else {
                    instance = ForgejoInstance(
                        serverURL: devServerURL, username: devUsername, name: "Dev",
                    )
                    modelContext.insert(instance)
                }
                authService.currentInstance = instance
                return true
            } catch {
                return false
            }
        }
    #endif

    private func switchInstance(to pending: PendingNavigation) async {
        let pendingURL = ForgejoClient.normalizeServerURL(pending.serverURL)
        let pendingUsername = pending.username
        let descriptor = FetchDescriptor<ForgejoInstance>(predicate: #Predicate<ForgejoInstance> {
            $0.serverURL == pendingURL && $0.username == pendingUsername
        })
        guard let instance = try? modelContext.fetch(descriptor).first else { return }
        try? await authService.restoreSession(instance: instance)
    }
}

#if DEBUG
    #Preview {
        ContentView(authService: AuthenticationService(), multiInstanceManager: .constant(nil))
            .modelContainer(for: ForgejoInstance.self, inMemory: true)
            .environment(NavigationState.shared)
    }
#endif
