import Foundation
import CoreData
import Combine

@MainActor
class AccountRepository: ObservableObject {
    private let context: NSManagedObjectContext

    @Published private(set) var accounts: [Account] = []

    private var cancellables = Set<AnyCancellable>()

    init(context: NSManagedObjectContext = CoreDataStack.shared.viewContext) {
        self.context = context
        fetchAccounts()
        setupNotificationObserver()
    }

    private func setupNotificationObserver() {
        NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange, object: context)
            .receive(on: DispatchQueue.main)
            .sink { [weak self] _ in
                self?.fetchAccounts()
            }
            .store(in: &cancellables)

        NotificationCenter.default.publisher(for: .cloudKitForceReload)
            .receive(on: DispatchQueue.main)
            .sink { [weak self] _ in self?.fetchAccounts() }
            .store(in: &cancellables)
    }

    // MARK: - Fetch

    func fetchAccounts() {
        let request: NSFetchRequest<Account> = Account.fetchRequest()
        request.sortDescriptors = [
            NSSortDescriptor(keyPath: \Account.sortOrder, ascending: true),
            NSSortDescriptor(keyPath: \Account.createdAt, ascending: true)
        ]
        // Performance: Add batch size for fetches
        request.fetchBatchSize = 50

        do {
            accounts = try context.fetch(request)
        } catch {
            print("Failed to fetch accounts: \(error)")
            accounts = []
        }
    }

    func fetchAccount(by id: UUID) -> Account? {
        let request: NSFetchRequest<Account> = Account.fetchRequest()
        request.predicate = NSPredicate(format: "id == %@", id as CVarArg)
        request.fetchLimit = 1
        return try? context.fetch(request).first
    }

    // MARK: - Create

    @discardableResult
    func createAccount(
        name: String,
        currency: String?,
        inputMode: InputMode,
        notificationFrequency: NotificationFrequency,
        customFrequencyMonths: Int = 1
    ) -> Account {
        let account = Account(context: context)
        account.name = name
        account.currency = currency
        account.inputMode = inputMode.rawValue
        account.notificationFrequency = notificationFrequency.rawValue
        account.customFrequencyMonths = Int16(customFrequencyMonths)
        account.sortOrder = Int16(accounts.count)
        save()
        return account
    }

    func createDefaultAccountIfNeeded() -> Account {
        // Fetch accounts directly from database to avoid race condition with async fetch
        let request: NSFetchRequest<Account> = Account.fetchRequest()
        request.sortDescriptors = [
            NSSortDescriptor(keyPath: \Account.sortOrder, ascending: true),
            NSSortDescriptor(keyPath: \Account.createdAt, ascending: true)
        ]
        let existingAccounts = (try? context.fetch(request)) ?? []

        // Check if Default account already exists
        if let defaultAccount = existingAccounts.first(where: { $0.isDefaultAccount }) {
            return defaultAccount
        }

        // If no Default account but other accounts exist, return first one
        if let existing = existingAccounts.first {
            return existing
        }

        // No accounts exist, create Default account
        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

        // Attach existing sources to the default account
        let sourceRequest: NSFetchRequest<InvestmentSource> = InvestmentSource.fetchRequest()
        if let sources = try? context.fetch(sourceRequest) {
            for source in sources where source.account == nil {
                source.account = account
            }
        }

        save()
        return account
    }

    /// Removes duplicate Default accounts, keeping only the oldest one.
    /// Call this once to clean up any duplicates created by the race condition bug.
    func cleanupDuplicateDefaultAccounts() {
        let request: NSFetchRequest<Account> = Account.fetchRequest()
        request.predicate = NSPredicate(format: "name == %@", Account.defaultAccountName)
        request.sortDescriptors = [NSSortDescriptor(keyPath: \Account.createdAt, ascending: true)]

        guard let defaultAccounts = try? context.fetch(request), defaultAccounts.count > 1 else {
            return
        }

        // Keep the first (oldest) Default account, delete the rest
        let accountsToDelete = defaultAccounts.dropFirst()
        for account in accountsToDelete {
            // Move sources to the kept Default account before deleting
            if let keptAccount = defaultAccounts.first {
                for source in account.sourcesArray {
                    source.account = keptAccount
                }
            }
            context.delete(account)
        }

        save()
    }

    // MARK: - Validation

    func isNameAvailable(_ name: String, excludingAccountId: UUID? = nil) -> Bool {
        let trimmed = name.trimmingCharacters(in: .whitespacesAndNewlines)
        let normalized = trimmed.lowercased()
        return !accounts.contains { account in
            if let excludeId = excludingAccountId, account.safeId == excludeId {
                return false
            }
            return account.name.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() == normalized
        }
    }

    // MARK: - Update

    func updateAccount(
        _ account: Account,
        name: String? = nil,
        currency: String? = nil,
        inputMode: InputMode? = nil,
        notificationFrequency: NotificationFrequency? = nil,
        customFrequencyMonths: Int? = nil
    ) {
        if let name = name {
            account.name = name
        }
        if let currency = currency {
            account.currency = currency
        }
        if let inputMode = inputMode {
            account.inputMode = inputMode.rawValue
        }
        if let notificationFrequency = notificationFrequency {
            account.notificationFrequency = notificationFrequency.rawValue
        }
        if let customMonths = customFrequencyMonths {
            account.customFrequencyMonths = Int16(customMonths)
        }
        save()
    }

    // MARK: - Delete

    func canDeleteAccount(_ account: Account) -> Bool {
        // Cannot delete the Default account
        guard !account.isDefaultAccount else { return false }
        // Must keep at least one account
        return accounts.count > 1
    }

    func deleteAccount(_ account: Account) {
        guard canDeleteAccount(account) else { return }
        context.delete(account)
        save()
    }

    // MARK: - Save

    private func save() {
        guard context.hasChanges else { return }
        do {
            try context.save()
            // Note: Removed redundant fetchAccounts() call - the NotificationCenter observer
            // in setupNotificationObserver() already handles refetching on context changes
        } catch {
            print("Failed to save accounts: \(error)")
        }
    }
}
