import Foundation
import CoreData

@objc(InvestmentSource)
public class InvestmentSource: NSManagedObject, Identifiable {
    @nonobjc public class func fetchRequest() -> NSFetchRequest<InvestmentSource> {
        return NSFetchRequest<InvestmentSource>(entityName: "InvestmentSource")
    }

    @NSManaged public var id: UUID
    @NSManaged public var name: String
    @NSManaged public var notificationFrequency: String
    @NSManaged public var customFrequencyMonths: Int16
    @NSManaged public var isActive: Bool
    @NSManaged public var monthlyContribution: NSDecimalNumber?
    @NSManaged public var createdAt: Date
    @NSManaged public var category: Category?
    @NSManaged public var account: Account?
    @NSManaged public var snapshots: NSSet?
    @NSManaged public var asset: Asset?

    public override func awakeFromInsert() {
        super.awakeFromInsert()
        id = UUID()
        createdAt = Date()
        isActive = true
        notificationFrequency = NotificationFrequency.monthly.rawValue
        customFrequencyMonths = 1
        name = ""
    }

    public override func awakeFromFetch() {
        super.awakeFromFetch()
        // Defensive: ensure id exists for legacy rows where id may be nil.
        if value(forKey: "id") == nil {
            setValue(UUID(), forKey: "id")
        }
    }
}

// MARK: - Notification Frequency

enum NotificationFrequency: String, CaseIterable, Identifiable {
    case monthly = "monthly"
    case quarterly = "quarterly"
    case semiannual = "semiannual"
    case annual = "annual"
    case custom = "custom"
    case never = "never"

    var id: String { rawValue }

    var displayName: String {
        switch self {
        case .monthly: return "Monthly"
        case .quarterly: return "Quarterly"
        case .semiannual: return "Semi-Annual"
        case .annual: return "Annual"
        case .custom: return "Custom"
        case .never: return "Never"
        }
    }

    var months: Int {
        switch self {
        case .monthly: return 1
        case .quarterly: return 3
        case .semiannual: return 6
        case .annual: return 12
        case .custom, .never: return 0
        }
    }
}

// MARK: - Computed Properties

extension InvestmentSource {
    /// Returns snapshots sorted by date descending (most recent first)
    /// Performance note: This sorts on every call. For repeated access, cache the result.
    var snapshotsArray: [Snapshot] {
        let set = snapshots as? Set<Snapshot> ?? []
        return set.sorted { $0.date > $1.date }
    }

    /// Returns snapshots sorted by date ascending (oldest first)
    /// Performance note: This sorts on every call. For repeated access, cache the result.
    var sortedSnapshotsByDateAscending: [Snapshot] {
        let set = snapshots as? Set<Snapshot> ?? []
        return set.sorted { $0.date < $1.date }
    }

    /// Returns the most recent snapshot without sorting all snapshots
    /// Performance: O(n) instead of O(n log n) for sorting
    var latestSnapshot: Snapshot? {
        guard let set = snapshots as? Set<Snapshot>, !set.isEmpty else { return nil }
        return set.max(by: { $0.date < $1.date })
    }

    var latestValue: Decimal {
        latestSnapshot?.value?.decimalValue ?? Decimal.zero
    }

    var snapshotCount: Int {
        snapshots?.count ?? 0
    }

    var frequency: NotificationFrequency {
        NotificationFrequency(rawValue: notificationFrequency) ?? .monthly
    }

    var nextReminderDate: Date? {
        if let account = account {
            return account.nextReminderDate
        }

        guard frequency != .never else { return nil }

        let months = frequency == .custom ? Int(customFrequencyMonths) : frequency.months
        guard let lastSnapshot = latestSnapshot else {
            return Date() // Remind now if no snapshots
        }

        return Calendar.current.date(byAdding: .month, value: months, to: lastSnapshot.date)
    }

    var needsUpdate: Bool {
        if let account = account {
            guard let nextDate = account.nextReminderDate else { return false }
            return Date() >= nextDate
        }

        guard let nextDate = nextReminderDate else { return false }
        return Date() >= nextDate
    }

    // MARK: - Performance Metrics

    /// Calculates total return percentage. Performance: Uses O(n) min/max instead of sorting.
    var totalReturn: Decimal {
        guard let set = snapshots as? Set<Snapshot>, set.count >= 2 else {
            return Decimal.zero
        }

        // Performance: Find min and max by date in one pass instead of sorting twice
        guard let first = set.min(by: { $0.date < $1.date }),
              let last = set.max(by: { $0.date < $1.date }),
              let firstValue = first.value?.decimalValue,
              firstValue != Decimal.zero else {
            return Decimal.zero
        }

        let lastValue = last.value?.decimalValue ?? Decimal.zero
        return ((lastValue - firstValue) / firstValue) * 100
    }

    /// Performance: Iterates snapshots directly without sorting
    var totalContributions: Decimal {
        guard let set = snapshots as? Set<Snapshot> else { return Decimal.zero }
        return set.reduce(Decimal.zero) { result, snapshot in
            result + (snapshot.contribution?.decimalValue ?? Decimal.zero)
        }
    }

}

// MARK: - Account Scheduling

extension Account {
    var nextReminderDate: Date? {
        guard frequency != .never else { return nil }

        let months = frequency == .custom ? Int(customFrequencyMonths) : frequency.months
        guard let latestSnapshotDate = sourcesArray
            .compactMap({ $0.latestSnapshot?.date })
            .max() else {
            return Date()
        }

        return Calendar.current.date(byAdding: .month, value: months, to: latestSnapshotDate)
    }
}

// MARK: - Generated accessors for snapshots

extension InvestmentSource {
    @objc(addSnapshotsObject:)
    @NSManaged public func addToSnapshots(_ value: Snapshot)

    @objc(removeSnapshotsObject:)
    @NSManaged public func removeFromSnapshots(_ value: Snapshot)

    @objc(addSnapshots:)
    @NSManaged public func addToSnapshots(_ values: NSSet)

    @objc(removeSnapshots:)
    @NSManaged public func removeFromSnapshots(_ values: NSSet)

}
