//
// OpenRocky — Voice-first AI Agent
// https://github.com/openrocky
//
// Developed by everettjf with the assistance of Claude Code and Codex.
// Date: 2026-03-25
// Copyright (c) 2026 everettjf. All rights reserved.
//

import AlarmKit
import Foundation
import SwiftUI

struct OpenRockyAlarmSnapshot: Codable, Sendable {
    let id: UUID
    let title: String
    let scheduledAt: String
    let repeats: String
    let authorizationState: String
}

@available(iOS 26.0, *)
@MainActor
final class OpenRockyAlarmService {
    /// Create a one-time or recurring alarm.
    /// - Parameters:
    ///   - title: Short label shown when the alarm fires.
    ///   - scheduledAt: Absolute fire date. For recurring alarms this provides the time-of-day;
    ///     the date component is ignored in favor of `repeatDays`.
    ///   - repeatDays: Weekdays the alarm should repeat on. Empty = one-shot.
    func createAlarm(
        title: String,
        scheduledAt: Date,
        repeatDays: [Locale.Weekday] = []
    ) async throws -> OpenRockyAlarmSnapshot {
        let manager = AlarmManager.shared
        let authorization = try await ensureAuthorized()

        let stopButton = AlarmButton(
            text: LocalizedStringResource(String.LocalizationValue("Stop")),
            textColor: .white,
            systemImageName: "stop.fill"
        )
        let presentation = AlarmPresentation(
            alert: .init(
                title: LocalizedStringResource(String.LocalizationValue(title)),
                stopButton: stopButton
            )
        )
        let attributes = AlarmAttributes(
            presentation: presentation,
            metadata: OpenRockyAlarmMetadata(title: title),
            tintColor: OpenRockyPalette.voicePrimary
        )

        let schedule: Alarm.Schedule
        let repeatsLabel: String
        if repeatDays.isEmpty {
            // One-time alarm — AlarmKit rejects past dates. Surface that clearly
            // rather than letting the error bubble as a generic framework failure.
            guard scheduledAt > Date() else {
                throw OpenRockyAlarmServiceError.timeInPast(scheduledAt.ISO8601Format())
            }
            schedule = .fixed(scheduledAt)
            repeatsLabel = "none"
        } else {
            let components = Calendar.current.dateComponents([.hour, .minute], from: scheduledAt)
            let hour = components.hour ?? 0
            let minute = components.minute ?? 0
            let time = Alarm.Schedule.Relative.Time(hour: hour, minute: minute)
            let relative = Alarm.Schedule.Relative(
                time: time,
                repeats: .weekly(repeatDays)
            )
            schedule = .relative(relative)
            repeatsLabel = repeatDays.map(Self.weekdayName).sorted().joined(separator: ",")
        }

        do {
            let alarm = try await manager.schedule(
                id: UUID(),
                configuration: .alarm(schedule: schedule, attributes: attributes)
            )
            return OpenRockyAlarmSnapshot(
                id: alarm.id,
                title: title,
                scheduledAt: scheduledAt.ISO8601Format(),
                repeats: repeatsLabel,
                authorizationState: String(describing: authorization)
            )
        } catch {
            rlog.error("AlarmKit schedule failed: \(error.localizedDescription)", category: "Tools")
            throw OpenRockyAlarmServiceError.scheduleFailed(error.localizedDescription)
        }
    }

    private func ensureAuthorized() async throws -> AlarmManager.AuthorizationState {
        let manager = AlarmManager.shared
        switch manager.authorizationState {
        case .authorized:
            return .authorized
        case .notDetermined:
            let authorization = try await manager.requestAuthorization()
            guard authorization == .authorized else {
                throw OpenRockyAlarmServiceError.permissionDenied
            }
            return authorization
        case .denied:
            throw OpenRockyAlarmServiceError.permissionDenied
        @unknown default:
            throw OpenRockyAlarmServiceError.permissionDenied
        }
    }

    /// Map common day strings (mon / monday / 一 / 周一 / 星期一) to Locale.Weekday.
    static func parseWeekdays(from tokens: [String]) -> [Locale.Weekday] {
        var result: [Locale.Weekday] = []
        for raw in tokens {
            let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
            guard !trimmed.isEmpty else { continue }
            if let weekday = weekdayMap[trimmed] {
                if !result.contains(weekday) { result.append(weekday) }
            }
        }
        return result
    }

    private static let weekdayMap: [String: Locale.Weekday] = {
        let entries: [(Locale.Weekday, [String])] = [
            (.monday, ["mon", "monday", "1", "一", "周一", "星期一", "礼拜一"]),
            (.tuesday, ["tue", "tues", "tuesday", "2", "二", "周二", "星期二", "礼拜二"]),
            (.wednesday, ["wed", "weds", "wednesday", "3", "三", "周三", "星期三", "礼拜三"]),
            (.thursday, ["thu", "thur", "thurs", "thursday", "4", "四", "周四", "星期四", "礼拜四"]),
            (.friday, ["fri", "friday", "5", "五", "周五", "星期五", "礼拜五"]),
            (.saturday, ["sat", "saturday", "6", "六", "周六", "星期六", "礼拜六"]),
            (.sunday, ["sun", "sunday", "7", "0", "日", "天", "周日", "周天", "星期日", "星期天", "礼拜日", "礼拜天"]),
        ]
        var map: [String: Locale.Weekday] = [:]
        for (weekday, keys) in entries {
            for key in keys { map[key] = weekday }
        }
        return map
    }()

    private static func weekdayName(_ weekday: Locale.Weekday) -> String {
        switch weekday {
        case .monday: "Mon"
        case .tuesday: "Tue"
        case .wednesday: "Wed"
        case .thursday: "Thu"
        case .friday: "Fri"
        case .saturday: "Sat"
        case .sunday: "Sun"
        @unknown default: "?"
        }
    }
}

@available(iOS 26.0, *)
private struct OpenRockyAlarmMetadata: AlarmMetadata {
    let title: String
}

enum OpenRockyAlarmServiceError: LocalizedError {
    case permissionDenied
    case timeInPast(String)
    case scheduleFailed(String)

    var errorDescription: String? {
        switch self {
        case .permissionDenied:
            "Alarm permission is required before OpenRocky can create an alarm. Enable it in Settings → Rocky → Notifications."
        case .timeInPast(let when):
            "Can't schedule an alarm in the past (\(when)). Pick a future time or pass repeat_days to make it recurring."
        case .scheduleFailed(let message):
            "AlarmKit rejected the schedule: \(message)"
        }
    }
}
