import Foundation
import Combine
import CoreData
import UIKit

@MainActor
class SettingsViewModel: ObservableObject {
    // MARK: - Published Properties

    @Published var isPremium = false
    @Published var isFamilyShared = false
    @Published var notificationsEnabled = false
    @Published var defaultNotificationTime = Date()
    @Published var analyticsEnabled = true
    @Published var currencyCode = AppSettings.getOrCreate(in: CoreDataStack.shared.viewContext).currency
    @Published var inputMode: InputMode = .simple

    @Published var isLoading = false
    @Published var showingPaywall = false
    @Published var showingExportOptions = false
    @Published var showingImportSheet = false
    @Published var showingResetConfirmation = false
    @Published var isBackupInProgress = false
    @Published var isRestoreInProgress = false
    @Published var errorMessage: String?
    @Published var successMessage: String?

    // MARK: - Statistics

    @Published var totalSources = 0
    @Published var totalSnapshots = 0
    @Published var totalCategories = 0

    // MARK: - Backups

    @Published var backupRetentionCount = 10
    @Published var backups: [BackupRecord] = []
    @Published var backupsEnabled = false

    // MARK: - Dependencies

    private let iapService: IAPService
    private let notificationService: NotificationService
    private let sourceRepository: InvestmentSourceRepository
    private let categoryRepository: CategoryRepository
    private let freemiumValidator: FreemiumValidator
    private var cancellables = Set<AnyCancellable>()
    private let backupsEnabledKey = "backupsEnabled"

    // MARK: - Initialization

    init(
        iapService: IAPService,
        notificationService: NotificationService? = nil,
        sourceRepository: InvestmentSourceRepository? = nil,
        categoryRepository: CategoryRepository? = nil
    ) {
        self.iapService = iapService
        self.notificationService = notificationService ?? .shared
        self.sourceRepository = sourceRepository ?? InvestmentSourceRepository()
        self.categoryRepository = categoryRepository ?? CategoryRepository()
        self.freemiumValidator = FreemiumValidator(iapService: iapService)

        setupObservers()
        loadSettings()
    }

    // MARK: - Setup

    private func setupObservers() {
        iapService.$isPremium
            .receive(on: DispatchQueue.main)
            .sink { [weak self] isPremium in
                self?.handlePremiumChange(isPremium)
            }
            .store(in: &cancellables)

        iapService.$isFamilyShared
            .receive(on: DispatchQueue.main)
            .assign(to: &$isFamilyShared)

        notificationService.$isAuthorized
            .receive(on: DispatchQueue.main)
            .assign(to: &$notificationsEnabled)
    }

    // MARK: - Data Loading

    func loadSettings() {
        let context = CoreDataStack.shared.viewContext
        let settings = AppSettings.getOrCreate(in: context)

        defaultNotificationTime = settings.defaultNotificationTime ?? Date()
        analyticsEnabled = settings.enableAnalytics
        currencyCode = settings.currency
        inputMode = InputMode(rawValue: settings.inputMode) ?? .simple
        backupRetentionCount = loadBackupRetention()
        backupsEnabled = loadBackupsEnabled()
        if backupsEnabled {
            refreshBackups()
        } else {
            backups = []
        }

        // Load statistics directly from database to avoid async race conditions
        loadStatistics()

        FirebaseService.shared.logScreenView(screenName: "Settings")
    }

    private func loadStatistics() {
        let context = CoreDataStack.shared.viewContext

        // Fetch source count directly
        let sourceRequest: NSFetchRequest<InvestmentSource> = InvestmentSource.fetchRequest()
        totalSources = (try? context.count(for: sourceRequest)) ?? 0

        // Fetch category count directly
        let categoryRequest: NSFetchRequest<Category> = Category.fetchRequest()
        totalCategories = (try? context.count(for: categoryRequest)) ?? 0

        // Fetch snapshot count directly
        let snapshotRequest: NSFetchRequest<Snapshot> = Snapshot.fetchRequest()
        totalSnapshots = (try? context.count(for: snapshotRequest)) ?? 0
    }

    // MARK: - Premium Actions

    func upgradeToPremium() {
        showingPaywall = true
        FirebaseService.shared.logPaywallShown(trigger: "settings_upgrade")
    }

    func restorePurchases() async {
        isLoading = true
        await iapService.restorePurchases()
        isLoading = false

        if isPremium {
            successMessage = "Purchases restored successfully!"
            FirebaseService.shared.logRestorePurchases(success: true)
        } else {
            errorMessage = "No purchases to restore"
            FirebaseService.shared.logRestorePurchases(success: false)
        }
    }

    // MARK: - Notification Settings

    func requestNotificationPermission() async {
        let granted = await notificationService.requestAuthorization()
        if !granted {
            errorMessage = "Please enable notifications in Settings"
        }
    }

    func updateNotificationTime(_ time: Date) {
        defaultNotificationTime = time

        let context = CoreDataStack.shared.viewContext
        let settings = AppSettings.getOrCreate(in: context)
        settings.defaultNotificationTime = time
        CoreDataStack.shared.save()

        // Reschedule all notifications with new time
        let sources = sourceRepository.fetchActiveSources()
        notificationService.scheduleAllReminders(for: sources)
    }

    func openSystemSettings() {
        if let url = URL(string: UIApplication.openSettingsURLString) {
            UIApplication.shared.open(url)
        }
    }

    // MARK: - Export

    @Published var isExporting = false
    @Published var exportProgress: Double = 0
    @Published var exportStatus = ""
    @Published var shareItem: ShareItem?

    struct ShareItem: Identifiable {
        let id = UUID()
        let url: URL
    }

    func exportData(format: ExportService.ExportFormat) {
        guard freemiumValidator.canExport() else {
            showingPaywall = true
            FirebaseService.shared.logPaywallShown(trigger: "export")
            return
        }

        isExporting = true
        exportProgress = 0
        exportStatus = "Preparing export..."

        Task {
            // Fetch data directly from database
            let context = CoreDataStack.shared.viewContext
            let sources = fetchAllSources(in: context)
            let categories = fetchAllCategories(in: context)

            await MainActor.run {
                exportProgress = 0.3
                exportStatus = "Generating \(format.rawValue) file..."
            }

            let content: String
            let fileName: String

            switch format {
            case .csv:
                content = ExportService.shared.exportToCSV(sources: sources, categories: categories)
                fileName = "investment_tracker_export.csv"
            case .json:
                content = ExportService.shared.exportToJSON(sources: sources, categories: categories)
                fileName = "investment_tracker_export.json"
            }

            await MainActor.run {
                exportProgress = 0.7
                exportStatus = "Saving file..."
            }

            // Create temporary file
            let tempURL = FileManager.default.temporaryDirectory
                .appendingPathComponent(fileName)

            do {
                try content.write(to: tempURL, atomically: true, encoding: .utf8)

                await MainActor.run {
                    exportProgress = 1.0
                    exportStatus = "Export complete"
                    isExporting = false
                    showingExportOptions = false
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
                        self?.shareItem = ShareItem(url: tempURL)
                    }
                    FirebaseService.shared.logExportAttempt(format: format.rawValue, success: true)
                }
            } catch {
                await MainActor.run {
                    isExporting = false
                    errorMessage = "Export failed: \(error.localizedDescription)"
                    FirebaseService.shared.logExportAttempt(format: format.rawValue, success: false)
                }
            }
        }
    }

    private func fetchAllSources(in context: NSManagedObjectContext) -> [InvestmentSource] {
        let request: NSFetchRequest<InvestmentSource> = InvestmentSource.fetchRequest()
        request.sortDescriptors = [NSSortDescriptor(keyPath: \InvestmentSource.name, ascending: true)]
        return (try? context.fetch(request)) ?? []
    }

    private func fetchAllCategories(in context: NSManagedObjectContext) -> [Category] {
        let request: NSFetchRequest<Category> = Category.fetchRequest()
        request.sortDescriptors = [NSSortDescriptor(keyPath: \Category.name, ascending: true)]
        return (try? context.fetch(request)) ?? []
    }

    // Share sheet is presented in SettingsView using shareItem.

    var canExport: Bool {
        freemiumValidator.canExport()
    }

    // MARK: - Analytics

    func toggleAnalytics(_ enabled: Bool) {
        analyticsEnabled = enabled

        let context = CoreDataStack.shared.viewContext
        let settings = AppSettings.getOrCreate(in: context)
        settings.enableAnalytics = enabled
        CoreDataStack.shared.save()

        // Note: In production, you'd also update Firebase Analytics consent
    }

    // MARK: - Currency

    func updateCurrency(_ code: String) {
        currencyCode = code
        let context = CoreDataStack.shared.viewContext
        let settings = AppSettings.getOrCreate(in: context)
        settings.currency = code
        CoreDataStack.shared.save()
    }

    // MARK: - Input Mode

    func updateInputMode(_ mode: InputMode) {
        inputMode = mode
        let context = CoreDataStack.shared.viewContext
        let settings = AppSettings.getOrCreate(in: context)
        settings.inputMode = mode.rawValue
        CoreDataStack.shared.save()
    }

    // MARK: - Data Management

    func resetAllData() {
        isLoading = true
        errorMessage = nil

        let context = CoreDataStack.shared.viewContext
        let entityNames = [
            "Snapshot",
            "InvestmentSource",
            "Category",
            "Asset",
            "Account",
            "Goal",
            "PredictionCache"
        ]

        context.performAndWait {
            var deletedObjectIDs: [NSManagedObjectID] = []
            for name in entityNames {
                let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: name)
                let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
                deleteRequest.resultType = .resultTypeObjectIDs
                if let result = try? context.execute(deleteRequest) as? NSBatchDeleteResult,
                   let objectIDs = result.result as? [NSManagedObjectID] {
                    deletedObjectIDs.append(contentsOf: objectIDs)
                }
            }

            if !deletedObjectIDs.isEmpty {
                NSManagedObjectContext.mergeChanges(
                    fromRemoteContextSave: [NSDeletedObjectsKey: deletedObjectIDs],
                    into: [context]
                )
            }
        }

        context.reset()

        context.performAndWait {
            // Recreate default categories
            let categoryCount = (try? context.count(for: Category.fetchRequest())) ?? 0
            if categoryCount == 0 {
                Category.createDefaultCategories(in: context)
            }

            // Recreate default account
            let accountCount = (try? context.count(for: Account.fetchRequest())) ?? 0
            if accountCount == 0 {
                let defaultCurrency = AppSettings.getOrCreate(in: context).currency
                let account = Account(context: context)
                account.name = Account.defaultAccountName
                account.currency = defaultCurrency
                account.inputMode = InputMode.simple.rawValue
                account.notificationFrequency = NotificationFrequency.monthly.rawValue
                account.customFrequencyMonths = 1
                account.sortOrder = 0
            }

            let settings = AppSettings.getOrCreate(in: context)
            settings.selectedAccountId = nil
            settings.showAllAccounts = true

            try? context.save()
        }

        // Clear notifications + monthly check-ins
        notificationService.cancelAllReminders()
        MonthlyCheckInStore.clearAll()
        CoreDataStack.shared.refreshWidgetData()

        loadSettings()
        isLoading = false
        successMessage = "All data has been reset"
        NotificationCenter.default.post(name: .didResetData, object: nil)
    }

    // MARK: - Backups

    func updateBackupRetention(_ count: Int) {
        guard backupsEnabled, isPremium else { return }
        backupRetentionCount = count
        UserDefaults.standard.set(count, forKey: "backupRetentionCount")
        refreshBackups()
    }

    func refreshBackups() {
        guard backupsEnabled, isPremium else { return }
        let includeICloud = UserDefaults.standard.bool(forKey: "cloudSyncEnabled")
        backups = BackupService.shared.listAllBackups(includeICloud: includeICloud)
    }

    func createBackupNow() {
        guard backupsEnabled, isPremium else { return }
        guard !isBackupInProgress else { return }
        isBackupInProgress = true
        errorMessage = nil

        Task {
            let includeICloud = UserDefaults.standard.bool(forKey: "cloudSyncEnabled")
            let records = BackupService.shared.createBackup(
                retentionCount: backupRetentionCount,
                includeICloud: includeICloud
            )
            await MainActor.run {
                backups = records
                isBackupInProgress = false
                successMessage = "Backup saved"
            }
        }
    }

    func restoreBackup(_ backup: BackupRecord) {
        guard backupsEnabled, isPremium else { return }
        guard !isRestoreInProgress else { return }
        isRestoreInProgress = true
        errorMessage = nil

        Task {
            let content: String
            do {
                content = try String(contentsOf: backup.url, encoding: .utf8)
            } catch {
                await MainActor.run {
                    isRestoreInProgress = false
                    errorMessage = "Failed to read backup file."
                }
                return
            }

            await MainActor.run {
                resetAllData()
            }

            let allowMultiple = iapService.isPremium
            let result = await ImportService.shared.importDataAsync(
                content: content,
                format: .json,
                allowMultipleAccounts: allowMultiple,
                defaultAccountName: Account.defaultAccountName,
                progress: { _ in }
            )

            await MainActor.run {
                isRestoreInProgress = false
                if result.errors.isEmpty {
                    successMessage = "Backup restored"
                } else {
                    errorMessage = "Backup restored with warnings."
                }
                loadSettings()
                refreshBackups()
            }
        }
    }

    // MARK: - Computed Properties

    var appVersion: String {
        "\(AppConstants.appVersion) (\(AppConstants.buildNumber))"
    }

    var premiumStatusText: String {
        if isPremium {
            return isFamilyShared ? "Premium (Family)" : "Premium"
        }
        return "Free"
    }

    var sourceLimitText: String {
        if isPremium {
            return "Unlimited"
        }
        return "\(totalSources)/\(FreemiumLimits.maxSources)"
    }

    var historyLimitText: String {
        if isPremium {
            return "Full history"
        }
        return "Last \(FreemiumLimits.maxHistoricalMonths) months"
    }

    var storageUsedText: String {
        let bytes = calculateStorageUsed()
        let formatter = ByteCountFormatter()
        formatter.countStyle = .file
        return formatter.string(fromByteCount: Int64(bytes))
    }

    private func calculateStorageUsed() -> Int {
        guard let storeURL = CoreDataStack.sharedStoreURL else { return 0 }

        do {
            let attributes = try FileManager.default.attributesOfItem(atPath: storeURL.path)
            return attributes[.size] as? Int ?? 0
        } catch {
            return 0
        }
    }

    private func loadBackupRetention() -> Int {
        let value = UserDefaults.standard.integer(forKey: "backupRetentionCount")
        let options = [5, 10, 20]
        return options.contains(value) ? value : 10
    }

    private func loadBackupsEnabled() -> Bool {
        let enabled = UserDefaults.standard.bool(forKey: backupsEnabledKey)
        if !isPremium && enabled {
            UserDefaults.standard.set(false, forKey: backupsEnabledKey)
            return false
        }
        return isPremium && enabled
    }

    private func handlePremiumChange(_ isPremium: Bool) {
        self.isPremium = isPremium
        if isPremium {
            backupsEnabled = UserDefaults.standard.bool(forKey: backupsEnabledKey)
            if backupsEnabled {
                refreshBackups()
            }
        } else {
            if backupsEnabled {
                backupsEnabled = false
            }
            UserDefaults.standard.set(false, forKey: backupsEnabledKey)
            backups = []
        }
    }

    func setBackupsEnabled(_ enabled: Bool) {
        guard isPremium else {
            backupsEnabled = false
            UserDefaults.standard.set(false, forKey: backupsEnabledKey)
            FirebaseService.shared.logPaywallShown(trigger: "backups")
            showingPaywall = true
            return
        }

        backupsEnabled = enabled
        UserDefaults.standard.set(enabled, forKey: backupsEnabledKey)
        if enabled {
            refreshBackups()
        } else {
            backups = []
        }
    }
}
