//
//  FoundationModelsInsightService.swift
//  Reflect
//
//  Created by Claude Code on 12/13/24.
//

import Foundation
import FoundationModels

/// Error types for insight generation
enum InsightGenerationError: Error, LocalizedError {
    case modelUnavailable(reason: String)
    case insufficientData
    case generationFailed(underlying: Error)
    case invalidResponse

    var errorDescription: String? {
        switch self {
        case .modelUnavailable(let reason):
            return "AI insights unavailable: \(reason)"
        case .insufficientData:
            return "Not enough mood data to generate insights"
        case .generationFailed(let error):
            return "Failed to generate insights: \(error.localizedDescription)"
        case .invalidResponse:
            return "Unable to parse AI response"
        }
    }
}

/// Service responsible for generating AI-powered mood insights using Apple's Foundation Models
@available(iOS 26, *)
@MainActor
class FoundationModelsInsightService: ObservableObject {

    // MARK: - Published State

    @Published private(set) var isAvailable: Bool = false
    @Published private(set) var isGenerating: Bool = false
    @Published private(set) var lastError: InsightGenerationError?

    // MARK: - Dependencies

    private let summarizer = MoodDataSummarizer()

    // MARK: - Cache

    private var cachedInsights: [String: (insights: [Insight], timestamp: Date)] = [:]
    private let cacheValidityDuration: TimeInterval = 3600 // 1 hour
    private var inProgressPeriods: Set<String> = []

    // MARK: - Initialization

    init() {
        checkAvailability()
    }

    /// Check if Foundation Models is available on this device
    func checkAvailability() {
        let model = SystemLanguageModel.default
        switch model.availability {
        case .available:
            isAvailable = true
        case .unavailable(let reason):
            isAvailable = false
            lastError = .modelUnavailable(reason: describeUnavailability(reason))
        @unknown default:
            isAvailable = false
            lastError = .modelUnavailable(reason: "Unknown availability status")
        }
    }

    private func describeUnavailability(_ reason: SystemLanguageModel.Availability.UnavailableReason) -> String {
        switch reason {
        case .deviceNotEligible:
            return "This device doesn't support Apple Intelligence"
        case .appleIntelligenceNotEnabled:
            return "Apple Intelligence is not enabled in Settings"
        case .modelNotReady:
            return "The AI model is still downloading"
        @unknown default:
            return "AI features are not available"
        }
    }

    /// Creates a new session for each request to allow concurrent generation
    private func createSession() -> LanguageModelSession {
        LanguageModelSession(instructions: systemInstructions)
    }

    // MARK: - System Instructions

    private var systemInstructions: String {
        let personalityPack = UserDefaultsStore.personalityPackable()

        switch personalityPack {
        case .Default:
            return defaultSystemInstructions
//        case .Rude:
//            return rudeSystemInstructions
        case .MotivationalCoach:
            return coachSystemInstructions
        case .ZenMaster:
            return zenSystemInstructions
        case .BestFriend:
            return bestFriendSystemInstructions
        case .DataAnalyst:
            return analystSystemInstructions
        }
    }

    private var defaultSystemInstructions: String {
        """
        You are a supportive mood analyst for the Reflect app. Analyze mood data and provide warm, actionable insights.

        Style: Encouraging, empathetic, concise (1-2 sentences per insight). Reference specific data.

        SF Symbols: star.fill, sun.max.fill, heart.fill, chart.line.uptrend.xyaxis, trophy.fill, calendar, leaf.fill, sparkles
        """
    }

    private var rudeSystemInstructions: String {
        """
        You are a brutally honest, sarcastic mood analyst. Think: judgmental friend meets disappointed therapist.

        Style: Backhanded compliments, dramatic disappointment, weaponize their own data against them. Use "Oh honey," "Congratulations," and "Shocking" sarcastically. Mock patterns, not pain.

        Examples: "THREE whole good days? Trophy's in the mail." / "Mondays destroy you. Revolutionary." / "Your mood tanks Sundays. Almost like you hate your job."

        SF Symbols: eye.fill, trophy.fill, flame.fill, exclamationmark.triangle.fill, theatermasks.fill, crown.fill
        """
    }

    private var coachSystemInstructions: String {
        """
        You are a HIGH ENERGY motivational coach analyzing mood data! Think Tony Robbins meets sports coach.

        Style: Enthusiastic, empowering, action-oriented! Use exclamations! Celebrate wins BIG, frame struggles as opportunities for GROWTH. Every insight ends with a call to action.

        Phrases: "Let's GO!", "Champion move!", "That's the winner's mindset!", "You're in the ZONE!", "Level up!"

        SF Symbols: figure.run, trophy.fill, flame.fill, bolt.fill, star.fill, flag.checkered, medal.fill
        """
    }

    private var zenSystemInstructions: String {
        """
        You are a calm, mindful Zen master reflecting on mood data. Think Buddhist monk meets gentle therapist.

        Style: Serene, philosophical, uses nature metaphors. Speak in calm, measured tones. Find wisdom in all emotions. No judgment, only observation and acceptance.

        Phrases: "Like the seasons...", "The river of emotion...", "In stillness we find...", "This too shall pass...", "With gentle awareness..."

        SF Symbols: leaf.fill, moon.fill, drop.fill, wind, cloud.fill, sunrise.fill, sparkles, peacesign
        """
    }

    private var bestFriendSystemInstructions: String {
        """
        You are their supportive best friend analyzing their mood data! Think caring bestie who's always got their back.

        Style: Warm, casual, uses "you" and "we" language. Validate feelings, celebrate with them, commiserate together. Use conversational tone with occasional gentle humor.

        Phrases: "Okay but...", "Not gonna lie...", "I see you!", "That's so valid!", "Girl/Dude...", "Honestly though..."

        SF Symbols: heart.fill, hand.thumbsup.fill, sparkles, star.fill, sun.max.fill, face.smiling.fill, balloon.fill
        """
    }

    private var analystSystemInstructions: String {
        """
        You are a clinical data analyst examining mood metrics. Think spreadsheet expert meets research scientist.

        Style: Objective, statistical, data-driven. Reference exact numbers, percentages, and trends. Avoid emotional language. Present findings like a research report.

        Phrases: "Data indicates...", "Statistically significant...", "Correlation observed...", "Trend analysis shows...", "Based on the metrics..."

        SF Symbols: chart.bar.fill, chart.line.uptrend.xyaxis, function, number, percent, chart.pie.fill, doc.text.magnifyingglass
        """
    }

    // MARK: - Insight Generation

    /// Generate AI-powered insights for the given mood entries
    /// - Parameters:
    ///   - entries: Array of mood entries to analyze
    ///   - periodName: The time period name (e.g., "this month", "this year", "all time")
    ///   - count: Number of insights to generate (default 5)
    ///   - healthAverages: Optional raw health data for AI to analyze correlations
    /// - Returns: Array of Insight objects
    func generateInsights(
        for entries: [MoodEntryModel],
        periodName: String,
        count: Int = 5,
        healthAverages: HealthService.HealthAverages? = nil
    ) async throws -> [Insight] {
        // Check cache first
        if let cached = cachedInsights[periodName],
           Date().timeIntervalSince(cached.timestamp) < cacheValidityDuration {
            return cached.insights
        }

        // Prevent duplicate concurrent generation for the same period
        guard !inProgressPeriods.contains(periodName) else {
            // Already generating for this period, wait for cache
            return cachedInsights[periodName]?.insights ?? []
        }
        inProgressPeriods.insert(periodName)
        defer { inProgressPeriods.remove(periodName) }

        guard isAvailable else {
            throw InsightGenerationError.modelUnavailable(reason: lastError?.localizedDescription ?? "Model not available")
        }

        // Create a new session for this request to allow concurrent generation
        let session = createSession()

        // Filter valid entries
        let validEntries = entries.filter { ![.missing, .placeholder].contains($0.mood) }

        guard !validEntries.isEmpty else {
            throw InsightGenerationError.insufficientData
        }

        isGenerating = true
        defer { isGenerating = false }

        // Prepare data summary with health data for AI analysis
        let summary = summarizer.summarize(entries: validEntries, periodName: periodName, healthAverages: healthAverages)
        let prompt = buildPrompt(from: summary, count: count)

        do {
            let response = try await session.respond(
                to: prompt,
                generating: AIInsightsResponse.self
            )

            let insights = response.content.insights.map { $0.toInsight() }

            // Cache results
            cachedInsights[periodName] = (insights, Date())

            return insights
        } catch {
            // Log detailed error for debugging
            print("❌ AI Insight generation failed for '\(periodName)': \(error)")
            print("   Error type: \(type(of: error))")
            print("   Localized: \(error.localizedDescription)")

            lastError = .generationFailed(underlying: error)
            throw lastError!
        }
    }

    // MARK: - Prompt Construction

    private func buildPrompt(from summary: MoodDataSummary, count: Int) -> String {
        let dataSection = summarizer.toPromptString(summary)

        return """
        Analyze this mood data and generate \(count) insights:

        \(dataSection)

        Include: 1 pattern, 1 advice, 1 prediction, and other varied insights. Reference specific data points.
        """
    }

    // MARK: - Cache Management

    /// Invalidate all cached insights
    func invalidateCache() {
        cachedInsights.removeAll()
    }

    /// Invalidate cached insights for a specific period
    func invalidateCache(for periodName: String) {
        cachedInsights.removeValue(forKey: periodName)
    }

    /// Check if insights are cached for a period
    func hasCachedInsights(for periodName: String) -> Bool {
        guard let cached = cachedInsights[periodName] else { return false }
        return Date().timeIntervalSince(cached.timestamp) < cacheValidityDuration
    }
}
