import Foundation

/// User-visible errors that can arise while handling session-related intents.
enum SessionIntentError: LocalizedError {
    case noRecentGym
    case noActiveSession
    case failedToStartSession
    case failedToEndSession

    var errorDescription: String? {
        switch self {
        case .noRecentGym:
            return "There's no recent gym to start a session with."
        case .noActiveSession:
            return "There isn't an active session to end right now."
        case .failedToStartSession:
            return "Ascently couldn't start a new session."
        case .failedToEndSession:
            return "Ascently couldn't finish the active session."
        }
    }
}

struct SessionIntentSummary: Sendable {
    let sessionId: UUID
    let gymName: String
    let status: SessionStatus
}

/// Controller for handling session operations from App Intents.
@MainActor
final class SessionIntentController {

    private let dataManager: ClimbingDataManager

    init(dataManager: ClimbingDataManager = .shared) {
        self.dataManager = dataManager
    }

    /// Starts a new session using the most recently visited gym.
    func startSessionWithLastUsedGym() async throws -> SessionIntentSummary {
        // Wait for data to load
        if dataManager.gyms.isEmpty {
            try? await Task.sleep(nanoseconds: 500_000_000)
        }

        guard let lastGym = dataManager.getLastUsedGym() else {
            logFailure(.noRecentGym, context: "No recorded sessions available")
            throw SessionIntentError.noRecentGym
        }

        guard let startedSession = await dataManager.startSessionAsync(gymId: lastGym.id) else {
            logFailure(.failedToStartSession, context: "Data manager failed to create new session")
            throw SessionIntentError.failedToStartSession
        }

        return SessionIntentSummary(
            sessionId: startedSession.id,
            gymName: lastGym.name,
            status: startedSession.status
        )
    }

    /// Ends the currently active climbing session, if one exists.
    func endActiveSession() async throws -> SessionIntentSummary {
        guard let activeSession = dataManager.activeSession else {
            logFailure(.noActiveSession, context: "No active session stored in data manager")
            throw SessionIntentError.noActiveSession
        }

        guard let completedSession = await dataManager.endSessionAsync(activeSession.id) else {
            logFailure(
                .failedToEndSession, context: "Data manager failed to complete active session")
            throw SessionIntentError.failedToEndSession
        }

        guard let gym = dataManager.gym(withId: completedSession.gymId) else {
            logFailure(
                .failedToEndSession,
                context: "Gym missing for completed session \(completedSession.id)")
            throw SessionIntentError.failedToEndSession
        }

        return SessionIntentSummary(
            sessionId: completedSession.id,
            gymName: gym.name,
            status: completedSession.status
        )
    }

    private func logFailure(_ error: SessionIntentError, context: String) {
        // Log error for debugging
        print("SessionIntentError: \(error). Context: \(context)")
    }

    /// Toggles the session state: ends active session if one exists, otherwise starts a new one.
    func toggleSession() async throws -> (summary: SessionIntentSummary, wasStarted: Bool) {
        // Wait for data to load
        if dataManager.gyms.isEmpty {
            try? await Task.sleep(nanoseconds: 500_000_000)
        }

        if dataManager.activeSession != nil {
            let summary = try await endActiveSession()
            return (summary, false)
        } else {
            let summary = try await startSessionWithLastUsedGym()
            return (summary, true)
        }
    }
}
