import Foundation
import Combine
import CoreData

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

    @Published var sources: [InvestmentSource] = []
    @Published var categories: [Category] = []
    @Published var selectedCategoryIds: Set<UUID> = []
    @Published var searchText = ""
    @Published var selectedAccount: Account?
    @Published var showAllAccounts = true
    @Published var isLoading = false
    @Published var showingAddSource = false
    @Published var showingPaywall = false
    @Published var errorMessage: String?

    // MARK: - Dependencies

    private let sourceRepository: InvestmentSourceRepository
    private let categoryRepository: CategoryRepository
    private let freemiumValidator: FreemiumValidator
    private var cancellables = Set<AnyCancellable>()

    // MARK: - Initialization

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

        setupObservers()
        loadData()
    }

    // MARK: - Setup

    private func setupObservers() {
        // Performance: Update categories separately (less frequent)
        categoryRepository.$categories
            .receive(on: DispatchQueue.main)
            .sink { [weak self] categories in
                self?.categories = categories
            }
            .store(in: &cancellables)

        // Performance: Combine all filter-triggering publishers into one stream
        // This prevents multiple rapid filter operations when state changes
        Publishers.CombineLatest4(
            sourceRepository.$sources,
            $searchText.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main),
            $selectedCategoryIds,
            $selectedAccount
        )
        .combineLatest($showAllAccounts)
        .debounce(for: .milliseconds(100), scheduler: DispatchQueue.main)
        .receive(on: DispatchQueue.main)
        .sink { [weak self] combined, _ in
            let (sources, _, _, _) = combined
            self?.filterAndSortSources(sources)
        }
        .store(in: &cancellables)
    }

    // MARK: - Data Loading

    func loadData() {
        isLoading = true
        categoryRepository.createDefaultCategoriesIfNeeded()
        sourceRepository.fetchSources()
        categoryRepository.fetchCategories()
        isLoading = false

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

    private func filterAndSortSources(_ allSources: [InvestmentSource]) {
        var filtered = allSources

        let selectedAccountId = selectedAccount?.safeId
        if !showAllAccounts, let selectedAccountId {
            filtered = filtered.filter { $0.account?.id == selectedAccountId }
        }

        // Filter by category (multi-select)
        if !selectedCategoryIds.isEmpty {
            filtered = filtered.filter {
                guard let id = $0.category?.id else { return false }
                return selectedCategoryIds.contains(id)
            }
        }

        // Filter by search text
        if !searchText.isEmpty {
            filtered = filtered.filter {
                $0.name.localizedCaseInsensitiveContains(searchText) ||
                ($0.category?.name.localizedCaseInsensitiveContains(searchText) ?? false)
            }
        }

        // Sort by value descending
        sources = filtered.sorted { $0.latestValue > $1.latestValue }
    }

    // MARK: - Actions

    func addSourceTapped() {
        if freemiumValidator.canAddSource(currentCount: sourceRepository.sourceCount) {
            showingAddSource = true
        } else {
            showingPaywall = true
            FirebaseService.shared.logPaywallShown(trigger: "source_limit")
        }
    }

    func createSource(
        name: String,
        category: Category,
        frequency: NotificationFrequency,
        customMonths: Int = 1,
        account: Account? = nil
    ) {
        let source = sourceRepository.createSource(
            name: name,
            category: category,
            notificationFrequency: frequency,
            customFrequencyMonths: customMonths,
            account: account
        )

        // Schedule notification
        NotificationService.shared.scheduleReminder(for: source)

        // Log analytics
        FirebaseService.shared.logSourceAdded(
            categoryName: category.name,
            sourceCount: sourceRepository.sourceCount
        )

        showingAddSource = false
    }

    func deleteSource(_ source: InvestmentSource) {
        let categoryName = source.category?.name ?? "Unknown"

        // Cancel notifications
        NotificationService.shared.cancelReminder(for: source)

        // Delete source
        sourceRepository.deleteSource(source)

        // Log analytics
        FirebaseService.shared.logSourceDeleted(categoryName: categoryName)
    }

    func deleteSource(at offsets: IndexSet) {
        for index in offsets {
            guard index < sources.count else { continue }
            deleteSource(sources[index])
        }
    }

    func toggleSourceActive(_ source: InvestmentSource) {
        sourceRepository.toggleActive(source)

        if source.isActive {
            NotificationService.shared.scheduleReminder(for: source)
        } else {
            NotificationService.shared.cancelReminder(for: source)
        }
    }

    // MARK: - Computed Properties

    var canAddSource: Bool {
        freemiumValidator.canAddSource(currentCount: sourceRepository.sourceCount)
    }

    var remainingSources: Int {
        freemiumValidator.remainingSources(currentCount: sourceRepository.sourceCount)
    }

    var sourceLimitReached: Bool {
        !canAddSource
    }

    var totalValue: Decimal {
        sources.reduce(Decimal.zero) { $0 + $1.latestValue }
    }

    var formattedTotalValue: String {
        totalValue.currencyString
    }

    var sourcesByCategory: [Category: [InvestmentSource]] {
        Dictionary(grouping: sources) { $0.category ?? categories.first! }
    }

    var isEmpty: Bool {
        sources.isEmpty && searchText.isEmpty && selectedCategoryIds.isEmpty
    }

    var isFiltered: Bool {
        !searchText.isEmpty || !selectedCategoryIds.isEmpty
    }

    // MARK: - Category Filter

    func selectCategory(_ category: Category?) {
        guard let category else {
            selectedCategoryIds = []
            return
        }
        if selectedCategoryIds.contains(category.id) {
            selectedCategoryIds.remove(category.id)
        } else {
            selectedCategoryIds.insert(category.id)
        }
    }

    func clearFilters() {
        searchText = ""
        selectedCategoryIds = []
    }
}
