//
//  AlarmKitService.swift
//  TheNoiseClock
//
//  Created by Matt Bruce on 2/2/26.
//

import ActivityKit
import AlarmKit
import Bedrock
import Foundation
import SwiftUI

/// Service for managing alarms using AlarmKit (iOS 26+).
/// AlarmKit alarms cut through Focus modes and silent mode.
@MainActor
final class AlarmKitService {
    
    // MARK: - Singleton
    
    static let shared = AlarmKitService()
    private let manager = AlarmManager.shared
    
    private init() {
        Design.debugLog("[alarmkit] AlarmKitService initialized")
        Design.debugLog("[alarmkit] Authorization state: \(manager.authorizationState)")
    }
    
    // MARK: - Authorization
    
    /// The current authorization state for AlarmKit
    var authorizationState: AlarmManager.AuthorizationState {
        manager.authorizationState
    }
    
    /// Request authorization to schedule alarms.
    /// - Returns: `true` if authorized, `false` otherwise.
    func requestAuthorization() async -> Bool {
        Design.debugLog("[alarmkit] Requesting authorization, current state: \(manager.authorizationState)")
        
        switch manager.authorizationState {
        case .notDetermined:
            do {
                let state = try await manager.requestAuthorization()
                Design.debugLog("[alarmkit] Authorization result: \(state)")
                return state == .authorized
            } catch {
                Design.debugLog("[alarmkit] Authorization error: \(error)")
                return false
            }
        case .authorized:
            Design.debugLog("[alarmkit] Already authorized")
            return true
        case .denied:
            Design.debugLog("[alarmkit] Authorization denied - user must enable in Settings")
            return false
        @unknown default:
            Design.debugLog("[alarmkit] Unknown authorization state")
            return false
        }
    }
    
    // MARK: - Scheduling
    
    /// Schedule an alarm using AlarmKit.
    /// - Parameter alarm: The alarm to schedule.
    func scheduleAlarm(_ alarm: Alarm) async throws {
        Design.debugLog("[alarmkit] ========== SCHEDULING ALARM ==========")
        Design.debugLog("[alarmkit] Label: \(alarm.label)")
        Design.debugLog("[alarmkit] Time: \(alarm.time)")
        Design.debugLog("[alarmkit] Sound: \(alarm.soundName)")
        Design.debugLog("[alarmkit] Volume: \(alarm.volume)")
        Design.debugLog("[alarmkit] ID: \(alarm.id)")
        
        // Ensure we're authorized
        if manager.authorizationState != .authorized {
            Design.debugLog("[alarmkit] Not authorized, requesting...")
            let authorized = await requestAuthorization()
            guard authorized else {
                Design.debugLog("[alarmkit] Authorization failed, cannot schedule alarm")
                throw AlarmKitError.notAuthorized
            }
        }
        
        // Create the stop button for the alarm
        let stopButton = AlarmButton(
            text: "Stop",
            textColor: .red,
            systemImageName: "stop.fill"
        )
        
        // Create the snooze button (secondary button with countdown behavior)
        let snoozeButton = AlarmButton(
            text: "Snooze",
            textColor: .white,
            systemImageName: "moon.zzz"
        )
        Design.debugLog("[alarmkit] Created stop and snooze buttons")
        
        // Create the alert presentation with snooze as secondary button
        // secondaryButtonBehavior: .countdown enables snooze functionality
        // Include both label and notification message in the title
        let alertTitle = alarm.notificationMessage.isEmpty 
            ? alarm.label 
            : "\(alarm.label) - \(alarm.notificationMessage)"
        let alert = AlarmPresentation.Alert(
            title: LocalizedStringResource(stringLiteral: alertTitle),
            stopButton: stopButton,
            secondaryButton: snoozeButton,
            secondaryButtonBehavior: .countdown
        )
        Design.debugLog("[alarmkit] Created alert with title: \(alertTitle)")
        
        // Create metadata for the alarm
        let metadata = NoiseClockAlarmMetadata(
            alarmId: alarm.id.uuidString,
            soundName: alarm.soundName,
            snoozeDuration: alarm.snoozeDuration,
            label: alarm.label,
            message: alarm.notificationMessage,
            volume: alarm.volume
        )
        Design.debugLog("[alarmkit] Created metadata: alarmId=\(metadata.alarmId), sound=\(metadata.soundName), message=\(metadata.message)")
        
        // Create alarm attributes
        let attributes = AlarmAttributes<NoiseClockAlarmMetadata>(
            presentation: AlarmPresentation(alert: alert),
            metadata: metadata,
            tintColor: Color.pink
        )
        Design.debugLog("[alarmkit] Created attributes with tint color")
        
        // Create the schedule - use fixed date for one-time alarms
        let schedule = createSchedule(for: alarm)
        Design.debugLog("[alarmkit] Created schedule")
        
        // CountdownDuration for snooze support:
        // - preAlert: nil = no countdown before alarm (fires immediately at scheduled time)
        // - postAlert: snooze duration (how long until alarm fires again after snooze)
        // If no snooze, set countdownDuration to nil
        let snoozeDurationSeconds = TimeInterval(alarm.snoozeDuration * 60)
        let countdownDuration: AlarmKit.Alarm.CountdownDuration? = snoozeDurationSeconds > 0
            ? AlarmKit.Alarm.CountdownDuration(preAlert: nil, postAlert: snoozeDurationSeconds)
            : nil
        Design.debugLog("[alarmkit] Countdown duration: preAlert=nil (immediate), postAlert=\(snoozeDurationSeconds)s (snooze)")
        
        // Create the sound
        let soundName = getSoundNameForAlarmKit(alarm.soundName)
        let alarmSound = AlertConfiguration.AlertSound.named(soundName)
        Design.debugLog("[alarmkit] Created sound: \(soundName)")
        
        // Create the alarm configuration with sound
        let configuration = AlarmManager.AlarmConfiguration<NoiseClockAlarmMetadata>(
            countdownDuration: countdownDuration,
            schedule: schedule,
            attributes: attributes,
            sound: alarmSound
        )
        Design.debugLog("[alarmkit] Created configuration with sound")
        
        // Schedule the alarm
        do {
            let scheduledAlarm = try await manager.schedule(
                id: alarm.id,
                configuration: configuration
            )
            Design.debugLog("[alarmkit] ✅ ALARM SCHEDULED SUCCESSFULLY")
            Design.debugLog("[alarmkit] Scheduled ID: \(scheduledAlarm.id)")
            Design.debugLog("[alarmkit] Scheduled state: \(scheduledAlarm.state)")
        } catch {
            Design.debugLog("[alarmkit] ❌ SCHEDULING FAILED: \(error)")
            throw AlarmKitError.schedulingFailed(error)
        }
    }
    
    // MARK: - Sound Configuration
    
    /// Get the sound name for AlarmKit and ensure it's in Library/Sounds
    /// AlarmKit can only play sounds from the main bundle root or Library/Sounds
    private func getSoundNameForAlarmKit(_ soundName: String) -> String {
        Design.debugLog("[alarmkit] Preparing sound for AlarmKit: \(soundName)")
        
        // Copy sound to Library/Sounds so AlarmKit can access it
        if copySoundToLibrarySounds(soundName) {
            Design.debugLog("[alarmkit] ✅ Sound ready in Library/Sounds: \(soundName)")
        } else {
            Design.debugLog("[alarmkit] ⚠️ Failed to copy sound to Library/Sounds, alarm may use default sound")
        }
        
        // AlarmKit expects just the filename (with extension) when in Library/Sounds
        return soundName
    }
    
    /// Copy a sound file from AlarmSounds folder to Library/Sounds
    /// Returns true if successful or file already exists
    private func copySoundToLibrarySounds(_ soundName: String) -> Bool {
        let fileManager = FileManager.default
        let nameWithoutExtension = (soundName as NSString).deletingPathExtension
        let ext = (soundName as NSString).pathExtension
        
        // Try multiple locations for the source sound file
        var sourceURL: URL?
        
        // 1. Try AlarmSounds subfolder in main bundle (Resources/AlarmSounds/)
        if let url = Bundle.main.url(forResource: nameWithoutExtension, withExtension: ext, subdirectory: "AlarmSounds") {
            sourceURL = url
            Design.debugLog("[alarmkit] Found sound in AlarmSounds subfolder: \(soundName)")
        }
        // 2. Try AlarmSounds.bundle
        else if let bundleURL = Bundle.main.url(forResource: "AlarmSounds", withExtension: "bundle"),
                let alarmBundle = Bundle(url: bundleURL),
                let url = alarmBundle.url(forResource: nameWithoutExtension, withExtension: ext) {
            sourceURL = url
            Design.debugLog("[alarmkit] Found sound in AlarmSounds.bundle: \(soundName)")
        }
        // 3. Try main bundle root
        else if let url = Bundle.main.url(forResource: nameWithoutExtension, withExtension: ext) {
            sourceURL = url
            Design.debugLog("[alarmkit] Found sound in main bundle root: \(soundName)")
        }
        
        guard let sourceURL = sourceURL else {
            Design.debugLog("[alarmkit] ❌ Sound file not found anywhere: \(soundName)")
            logAvailableAlarmSounds()
            return false
        }
        
        // Get destination URL in Library/Sounds
        guard let libraryURL = fileManager.urls(for: .libraryDirectory, in: .userDomainMask).first else {
            Design.debugLog("[alarmkit] ❌ Could not get Library directory")
            return false
        }
        
        let soundsDirectory = libraryURL.appendingPathComponent("Sounds")
        let destinationURL = soundsDirectory.appendingPathComponent(soundName)
        
        // Create Sounds directory if it doesn't exist
        if !fileManager.fileExists(atPath: soundsDirectory.path) {
            do {
                try fileManager.createDirectory(at: soundsDirectory, withIntermediateDirectories: true)
                Design.debugLog("[alarmkit] Created Library/Sounds directory")
            } catch {
                Design.debugLog("[alarmkit] ❌ Failed to create Sounds directory: \(error)")
                return false
            }
        }
        
        // Copy file if it doesn't exist or is different
        if fileManager.fileExists(atPath: destinationURL.path) {
            // Check if source is newer (simple check - could compare file sizes/hashes)
            Design.debugLog("[alarmkit] Sound already exists in Library/Sounds: \(soundName)")
            return true
        }
        
        do {
            try fileManager.copyItem(at: sourceURL, to: destinationURL)
            Design.debugLog("[alarmkit] ✅ Copied sound to Library/Sounds: \(soundName)")
            return true
        } catch {
            Design.debugLog("[alarmkit] ❌ Failed to copy sound: \(error)")
            return false
        }
    }
    
    /// Log available sound files in Library/Sounds for debugging
    private func logLibrarySounds() {
        guard let libraryURL = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first else {
            return
        }
        
        let soundsDirectory = libraryURL.appendingPathComponent("Sounds")
        Design.debugLog("[alarmkit] ========== LIBRARY/SOUNDS FILES ==========")
        
        do {
            let files = try FileManager.default.contentsOfDirectory(atPath: soundsDirectory.path)
            Design.debugLog("[alarmkit] Files in Library/Sounds: \(files)")
        } catch {
            Design.debugLog("[alarmkit] Library/Sounds directory doesn't exist or is empty")
        }
    }
    
    /// Log available alarm sounds in the bundle for debugging
    private func logAvailableAlarmSounds() {
        Design.debugLog("[alarmkit] ========== AVAILABLE ALARM SOUNDS ==========")
        
        // Check AlarmSounds subfolder
        if let resourcePath = Bundle.main.resourcePath {
            let alarmSoundsPath = (resourcePath as NSString).appendingPathComponent("AlarmSounds")
            if FileManager.default.fileExists(atPath: alarmSoundsPath) {
                do {
                    let files = try FileManager.default.contentsOfDirectory(atPath: alarmSoundsPath)
                    Design.debugLog("[alarmkit] Files in AlarmSounds folder: \(files)")
                } catch {
                    Design.debugLog("[alarmkit] Error reading AlarmSounds folder: \(error)")
                }
            } else {
                Design.debugLog("[alarmkit] AlarmSounds folder doesn't exist")
            }
        }
        
        // Check AlarmSounds.bundle
        if let bundleURL = Bundle.main.url(forResource: "AlarmSounds", withExtension: "bundle"),
           let alarmBundle = Bundle(url: bundleURL),
           let bundlePath = alarmBundle.resourcePath {
            do {
                let files = try FileManager.default.contentsOfDirectory(atPath: bundlePath)
                Design.debugLog("[alarmkit] Files in AlarmSounds.bundle: \(files)")
            } catch {
                Design.debugLog("[alarmkit] Error reading AlarmSounds.bundle: \(error)")
            }
        }
    }
    
    /// Cancel a scheduled alarm.
    /// - Parameter id: The UUID of the alarm to cancel.
    func cancelAlarm(id: UUID) {
        Design.debugLog("[alarmkit] Cancelling alarm: \(id)")
        do {
            try manager.cancel(id: id)
            Design.debugLog("[alarmkit] ✅ Alarm cancelled: \(id)")
        } catch {
            Design.debugLog("[alarmkit] ❌ Cancel error: \(error)")
        }
    }
    
    /// Stop an active alarm that is currently alerting.
    /// - Parameter id: The UUID of the alarm to stop.
    func stopAlarm(id: UUID) {
        Design.debugLog("[alarmkit] Stopping alarm: \(id)")
        do {
            try manager.stop(id: id)
            Design.debugLog("[alarmkit] ✅ Alarm stopped: \(id)")
        } catch {
            Design.debugLog("[alarmkit] ❌ Stop error: \(error)")
        }
    }
    
    /// Snooze an active alarm by starting its countdown again.
    /// - Parameter id: The UUID of the alarm to snooze.
    func snoozeAlarm(id: UUID) {
        Design.debugLog("[alarmkit] Snoozing alarm: \(id)")
        do {
            try manager.countdown(id: id)
            Design.debugLog("[alarmkit] ✅ Alarm snoozed: \(id)")
        } catch {
            Design.debugLog("[alarmkit] ❌ Snooze error: \(error)")
        }
    }
    
    // MARK: - Alarm Updates
    
    /// Async sequence that emits the current set of alarms whenever changes occur.
    var alarmUpdates: some AsyncSequence<[AlarmKit.Alarm], Never> {
        manager.alarmUpdates
    }
    
    /// Log current state of all scheduled alarms
    func logCurrentAlarms() {
        Design.debugLog("[alarmkit] ========== CURRENT ALARMS ==========")
        Task {
            for await alarms in manager.alarmUpdates {
                Design.debugLog("[alarmkit] Found \(alarms.count) alarm(s) in AlarmKit")
                for alarm in alarms {
                    Design.debugLog("[alarmkit]   - ID: \(alarm.id)")
                    Design.debugLog("[alarmkit]     State: \(alarm.state)")
                }
                break // Just log once
            }
        }
    }
    
    // MARK: - Private Methods
    
    /// Create an AlarmKit schedule from an Alarm model.
    private func createSchedule(for alarm: Alarm) -> AlarmKit.Alarm.Schedule {
        // Log the raw alarm time
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
        Design.debugLog("[alarmkit] Raw alarm.time: \(formatter.string(from: alarm.time))")
        
        // Calculate the next trigger time
        let triggerDate = alarm.nextTriggerTime()
        
        Design.debugLog("[alarmkit] Next trigger date: \(formatter.string(from: triggerDate))")
        Design.debugLog("[alarmkit] Current time: \(formatter.string(from: Date.now))")
        
        let secondsUntil = triggerDate.timeIntervalSinceNow
        let minutesUntil = secondsUntil / 60
        Design.debugLog("[alarmkit] Time until alarm: \(Int(secondsUntil)) seconds (\(String(format: "%.1f", minutesUntil)) minutes)")
        
        // Warn if the alarm is too far in the future (might indicate wrong date calculation)
        if secondsUntil > 86400 {
            Design.debugLog("[alarmkit] ⚠️ WARNING: Alarm is more than 24 hours away!")
        }
        
        // Use fixed schedule for one-time alarms
        let schedule = AlarmKit.Alarm.Schedule.fixed(triggerDate)
        
        Design.debugLog("[alarmkit] Schedule created: fixed at \(formatter.string(from: triggerDate))")
        return schedule
    }
}

// MARK: - Errors

enum AlarmKitError: Error, LocalizedError {
    case notAuthorized
    case schedulingFailed(Error)
    
    var errorDescription: String? {
        switch self {
        case .notAuthorized:
            return "AlarmKit is not authorized. Please enable alarm permissions in Settings."
        case .schedulingFailed(let error):
            return "Failed to schedule alarm: \(error.localizedDescription)"
        }
    }
}
