import Foundation
import Combine
import CoreData

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

    @Published var source: InvestmentSource
    @Published var snapshots: [Snapshot] = []
    @Published var metrics: InvestmentMetrics = .empty
    @Published var predictions: [Prediction] = []
    @Published var predictionResult: PredictionResult?
    @Published var isDeleted = false

    @Published var isLoading = false
    @Published var showingAddSnapshot = false
    @Published var showingEditSource = false
    @Published var showingPaywall = false
    @Published var errorMessage: String?

    // MARK: - Chart Data

    @Published var chartData: [(date: Date, value: Decimal)] = []

    // MARK: - Dependencies

    private let snapshotRepository: SnapshotRepository
    private let sourceRepository: InvestmentSourceRepository
    private let calculationService: CalculationService
    private let predictionEngine: PredictionEngine
    private let freemiumValidator: FreemiumValidator
    private let iapService: IAPService
    private var cancellables = Set<AnyCancellable>()
    private var isRefreshing = false
    private var refreshQueued = false
    private var refreshTask: Task<Void, Never>?
    private let sourceName: String

    // MARK: - Initialization

    init(
        source: InvestmentSource,
        snapshotRepository: SnapshotRepository? = nil,
        sourceRepository: InvestmentSourceRepository? = nil,
        calculationService: CalculationService? = nil,
        predictionEngine: PredictionEngine? = nil,
        iapService: IAPService
    ) {
        self.source = source
        self.sourceName = source.name
        self.snapshotRepository = snapshotRepository ?? SnapshotRepository()
        self.sourceRepository = sourceRepository ?? InvestmentSourceRepository()
        self.calculationService = calculationService ?? .shared
        self.predictionEngine = predictionEngine ?? .shared
        self.freemiumValidator = FreemiumValidator(iapService: iapService)
        self.iapService = iapService

        loadData()
        setupObservers()
    }

    // MARK: - Setup

    private func setupObservers() {
        NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange)
            .debounce(for: .milliseconds(300), scheduler: DispatchQueue.main)
            .sink { [weak self] notification in
                guard let self,
                      self.isRelevantChange(notification) else { return }
                self.refreshData()
            }
            .store(in: &cancellables)

        iapService.$isPremium
            .removeDuplicates()
            .receive(on: DispatchQueue.main)
            .sink { [weak self] _ in
                self?.refreshData()
            }
            .store(in: &cancellables)
    }

    // MARK: - Data Loading

    func loadData() {
        isLoading = true
        refreshData()
        isLoading = false

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

    func refreshData() {
        refreshQueued = true
        guard !isRefreshing else { return }
        isRefreshing = true

        // Cancel any previous refresh task to avoid stale updates
        refreshTask?.cancel()

        refreshTask = Task { [weak self] in
            guard let self else { return }
            guard !self.isDeleted, !self.source.isDeleted, self.source.managedObjectContext != nil else {
                self.isDeleted = true
                self.isRefreshing = false
                return
            }
            while self.refreshQueued && !Task.isCancelled {
                self.refreshQueued = false

                // Fetch snapshots (filtered by freemium limits)
                let allSnapshots = snapshotRepository.fetchSnapshots(for: source)
                let filteredSnapshots = freemiumValidator.filterSnapshots(allSnapshots)

                // Check for cancellation before updating UI
                guard !Task.isCancelled else { break }

                // Performance: Only update if data actually changed
                let snapshotsChanged = filteredSnapshots.count != self.snapshots.count ||
                    filteredSnapshots.first?.date != self.snapshots.first?.date

                if snapshotsChanged {
                    self.snapshots = filteredSnapshots

                    // Calculate metrics
                    self.metrics = calculationService.calculateMetrics(for: filteredSnapshots)

                    // Performance: Pre-sort once and reuse
                    let sortedSnapshots = filteredSnapshots.sorted { $0.date < $1.date }

                    // Prepare chart data - avoid creating new array if possible
                    self.chartData = sortedSnapshots.map { (date: $0.date, value: $0.decimalValue) }

                    // Calculate predictions if premium - only if we have enough data
                    if freemiumValidator.canViewPredictions() && sortedSnapshots.count >= 3 {
                        self.predictionResult = predictionEngine.predict(snapshots: sortedSnapshots)
                        self.predictions = self.predictionResult?.predictions ?? []
                    } else {
                        self.predictions = []
                        self.predictionResult = nil
                    }
                }

            }
            self.isRefreshing = false
        }
    }

    /// Cancel any ongoing background tasks - call when view disappears
    func cancelPendingTasks() {
        refreshTask?.cancel()
        refreshTask = nil
    }

    // MARK: - Snapshot Actions

    func addSnapshot(date: Date, value: Decimal, contribution: Decimal?, notes: String?) {
        snapshotRepository.createSnapshot(
            for: source,
            date: date,
            value: value,
            contribution: contribution,
            notes: notes
        )

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

        // Log analytics
        FirebaseService.shared.logSnapshotAdded(sourceName: source.name, value: value)

        showingAddSnapshot = false
        refreshData()
    }

    func deleteSnapshot(_ snapshot: Snapshot) {
        snapshotRepository.deleteSnapshot(snapshot)
        refreshData()
    }

    func deleteSnapshot(at offsets: IndexSet) {
        snapshotRepository.deleteSnapshot(at: offsets, from: snapshots)
        refreshData()
    }

    // MARK: - Source Actions

    func updateSource(
        name: String,
        category: Category,
        frequency: NotificationFrequency,
        customMonths: Int
    ) {
        sourceRepository.updateSource(
            source,
            name: name,
            category: category,
            notificationFrequency: frequency,
            customFrequencyMonths: customMonths
        )

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

        showingEditSource = false
    }

    func deleteSource() {
        guard !isDeleted else { return }
        // Mark deleted first so the view can dismiss before any further access.
        isDeleted = true
        snapshots = []
        chartData = []
        predictionResult = nil
        // Cancel any pending notifications for this source
        NotificationService.shared.cancelReminder(for: source)

        // Log analytics before deletion
        FirebaseService.shared.logSourceDeleted(categoryName: source.category?.name ?? "Uncategorized")

        // Delete the source using the existing repository
        sourceRepository.deleteSource(source)
    }

    // MARK: - Predictions

    func showPredictions() {
        if freemiumValidator.canViewPredictions() {
            // Already loaded, just navigate
            FirebaseService.shared.logPredictionViewed(
                algorithm: predictionResult?.algorithm.rawValue ?? "unknown"
            )
        } else {
            showingPaywall = true
            FirebaseService.shared.logPaywallShown(trigger: "predictions")
        }
    }

    // MARK: - Computed Properties

    var currentValue: Decimal {
        source.latestValue
    }

    var formattedCurrentValue: String {
        currentValue.currencyString
    }

    var safeSourceName: String {
        if source.isDeleted || source.managedObjectContext == nil {
            return sourceName
        }
        return source.name
    }

    var totalReturn: Decimal {
        metrics.absoluteReturn
    }

    var formattedTotalReturn: String {
        metrics.formattedAbsoluteReturn
    }

    var percentageReturn: Decimal {
        metrics.percentageReturn
    }

    var formattedPercentageReturn: String {
        metrics.formattedPercentageReturn
    }

    var isPositiveReturn: Bool {
        totalReturn >= 0
    }

    var categoryName: String {
        source.category?.name ?? "Uncategorized"
    }

    var categoryColor: String {
        source.category?.colorHex ?? "#3B82F6"
    }

    var lastUpdated: String {
        source.latestSnapshot?.date.friendlyDescription ?? "Never"
    }

    var snapshotCount: Int {
        snapshots.count
    }

    var hasEnoughDataForPredictions: Bool {
        snapshots.count >= 3
    }

    var canViewPredictions: Bool {
        freemiumValidator.canViewPredictions()
    }

    var isHistoryLimited: Bool {
        !freemiumValidator.isPremium
    }

    var hiddenSnapshotCount: Int {
        // For free users, count how many snapshots are hidden due to the 12-month limit
        guard !freemiumValidator.isPremium else { return 0 }

        // Get all snapshots without the date filter
        let allSnapshots = snapshotRepository.fetchSnapshots(for: source)
        return max(0, allSnapshots.count - snapshots.count)
    }

    var visibleSnapshots: [Snapshot] {
        // Premium users see all snapshots (already filtered by freemiumValidator in refreshData)
        // Free users also see all their filtered snapshots (limited to last 12 months)
        snapshots
    }

    private func isRelevantChange(_ notification: Notification) -> Bool {
        guard let info = notification.userInfo else { return false }
        let keys: [String] = [
            NSInsertedObjectsKey,
            NSUpdatedObjectsKey,
            NSDeletedObjectsKey,
            NSRefreshedObjectsKey
        ]

        for key in keys {
            if let objects = info[key] as? Set<NSManagedObject> {
                if objects.contains(where: { obj in
                    if let snapshot = obj as? Snapshot {
                        return snapshot.source?.id == source.id
                    }
                    if let investmentSource = obj as? InvestmentSource {
                        return investmentSource.id == source.id
                    }
                    return false
                }) {
                    return true
                }
            }
        }
        return false
    }
}
