import Foundation
import CoreData

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

    @NSManaged public var id: UUID
    @NSManaged public var sourceId: UUID?
    @NSManaged public var algorithm: String
    @NSManaged public var predictionData: Data?
    @NSManaged public var calculatedAt: Date
    @NSManaged public var validUntil: Date

    public override func awakeFromInsert() {
        super.awakeFromInsert()
        id = UUID()
        calculatedAt = Date()
        // Cache valid for 24 hours
        validUntil = Calendar.current.date(byAdding: .hour, value: 24, to: Date()) ?? Date()
        algorithm = PredictionAlgorithm.linear.rawValue
    }
}

// MARK: - Prediction Algorithm

enum PredictionAlgorithm: String, CaseIterable, Identifiable {
    case linear = "linear"
    case exponentialSmoothing = "exponential_smoothing"
    case movingAverage = "moving_average"
    case holtTrend = "holt_trend"

    var id: String { rawValue }

    var displayName: String {
        switch self {
        case .linear: return "Linear Regression"
        case .exponentialSmoothing: return "Exponential Smoothing"
        case .movingAverage: return "Moving Average"
        case .holtTrend: return "Holt Trend"
        }
    }

    var description: String {
        switch self {
        case .linear:
            return "Projects future values based on historical trend line"
        case .exponentialSmoothing:
            return "Gives more weight to recent data points"
        case .movingAverage:
            return "Smooths out short-term fluctuations"
        case .holtTrend:
            return "Captures level and trend changes over time"
        }
    }
}

// MARK: - Computed Properties

extension PredictionCache {
    var isValid: Bool {
        Date() < validUntil
    }

    var algorithmType: PredictionAlgorithm {
        PredictionAlgorithm(rawValue: algorithm) ?? .linear
    }

    var predictions: [Prediction]? {
        guard let data = predictionData else { return nil }
        return try? JSONDecoder().decode([Prediction].self, from: data)
    }
}

// MARK: - Static Helpers

extension PredictionCache {
    static func getCache(
        for sourceId: UUID?,
        algorithm: PredictionAlgorithm,
        in context: NSManagedObjectContext
    ) -> PredictionCache? {
        let request: NSFetchRequest<PredictionCache> = PredictionCache.fetchRequest()

        if let sourceId = sourceId {
            request.predicate = NSPredicate(
                format: "sourceId == %@ AND algorithm == %@",
                sourceId as CVarArg,
                algorithm.rawValue
            )
        } else {
            request.predicate = NSPredicate(
                format: "sourceId == nil AND algorithm == %@",
                algorithm.rawValue
            )
        }

        request.fetchLimit = 1

        guard let cache = try? context.fetch(request).first,
              cache.isValid else {
            return nil
        }

        return cache
    }

    static func saveCache(
        for sourceId: UUID?,
        algorithm: PredictionAlgorithm,
        predictions: [Prediction],
        in context: NSManagedObjectContext
    ) {
        // Remove old cache
        let request: NSFetchRequest<PredictionCache> = PredictionCache.fetchRequest()
        if let sourceId = sourceId {
            request.predicate = NSPredicate(
                format: "sourceId == %@ AND algorithm == %@",
                sourceId as CVarArg,
                algorithm.rawValue
            )
        } else {
            request.predicate = NSPredicate(
                format: "sourceId == nil AND algorithm == %@",
                algorithm.rawValue
            )
        }

        if let oldCache = try? context.fetch(request) {
            oldCache.forEach { context.delete($0) }
        }

        // Create new cache
        let cache = PredictionCache(context: context)
        cache.sourceId = sourceId
        cache.algorithm = algorithm.rawValue
        cache.predictionData = try? JSONEncoder().encode(predictions)

        try? context.save()
    }

    static func invalidateAll(in context: NSManagedObjectContext) {
        let request: NSFetchRequest<PredictionCache> = PredictionCache.fetchRequest()
        if let caches = try? context.fetch(request) {
            caches.forEach { context.delete($0) }
        }
        try? context.save()
    }
}
