// Sources/Core/Sessions/SessionRegistry.swift
// T-2.6: Session listing, creation, and deletion via the sidecar REST API.

import Foundation

// MARK: - SessionInfo

struct SessionInfo: Identifiable, Hashable, Sendable {
    let id: String
    let name: String
    let state: String  // "running", "idle", etc.
}

// MARK: - SessionRegistry

@MainActor
final class SessionRegistry: ObservableObject {
    @Published var sessions: [SessionInfo] = []
    @Published var isLoading = false
    @Published var error: String? = nil

    init() {}

    // MARK: - Private helpers

    private func baseURL(credential: SidecarCredential) -> URL {
        URL(string: "http://\(credential.host):\(credential.port)")!
    }

    private func authorizedRequest(url: URL, credential: SidecarCredential) -> URLRequest {
        var req = URLRequest(url: url)
        req.setValue("Bearer \(credential.bearerToken)", forHTTPHeaderField: "Authorization")
        return req
    }

    // MARK: - GET /sessions

    /// Fetches the current session list from the sidecar and updates `sessions`.
    func refresh(credential: SidecarCredential) async {
        isLoading = true
        error = nil
        defer { isLoading = false }

        let url = baseURL(credential: credential).appendingPathComponent("sessions")
        let req = authorizedRequest(url: url, credential: credential)

        do {
            let (data, _) = try await URLSession.shared.data(for: req)
            let items = try JSONDecoder().decode([SessionItem].self, from: data)
            sessions = items.map { SessionInfo(id: $0.id, name: $0.name, state: $0.state) }
        } catch {
            self.error = error.localizedDescription
        }
    }

    // MARK: - POST /sessions

    /// Creates a new session with the given name and returns the created `SessionInfo`.
    @discardableResult
    func spawnSession(name: String, credential: SidecarCredential) async throws -> SessionInfo {
        let url = baseURL(credential: credential).appendingPathComponent("sessions")
        var req = authorizedRequest(url: url, credential: credential)
        req.httpMethod = "POST"
        req.setValue("application/json", forHTTPHeaderField: "Content-Type")
        req.httpBody = try JSONEncoder().encode(["name": name])

        let (data, response) = try await URLSession.shared.data(for: req)

        guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {
            throw SessionRegistryError.unexpectedStatus
        }

        let item = try JSONDecoder().decode(SessionItem.self, from: data)
        let info = SessionInfo(id: item.id, name: item.name, state: item.state)

        // Append optimistically; a follow-up refresh will normalise order.
        sessions.append(info)
        return info
    }

    // MARK: - DELETE /sessions/<id>

    /// Deletes the session with the given id.
    func deleteSession(id: String, credential: SidecarCredential) async throws {
        let url = baseURL(credential: credential)
            .appendingPathComponent("sessions")
            .appendingPathComponent(id)
        var req = authorizedRequest(url: url, credential: credential)
        req.httpMethod = "DELETE"

        let (_, response) = try await URLSession.shared.data(for: req)

        guard let http = response as? HTTPURLResponse, (200..<300).contains(http.statusCode) else {
            throw SessionRegistryError.unexpectedStatus
        }

        sessions.removeAll { $0.id == id }
    }
}

// MARK: - Private Codable helper

private struct SessionItem: Decodable {
    let id: String
    let name: String
    let state: String
}

// MARK: - Errors

enum SessionRegistryError: LocalizedError {
    case unexpectedStatus

    var errorDescription: String? {
        switch self {
        case .unexpectedStatus:
            return "Unexpected HTTP status from sidecar."
        }
    }
}
