import Foundation

/// Cliente de API para comunicarse con el backend VPS.
///
/// `VPSAPIClient` proporciona una interfaz completa para interactuar con el backend
/// que gestiona el almacenamiento y serving de capítulos de manga en un VPS.
///
/// El cliente implementa:
/// - Request de descarga de capítulos al VPS
/// - Verificación de disponibilidad de capítulos
/// - Listado de capítulos descargados
/// - Obtención de URLs de imágenes
/// - Eliminación de capítulos del VPS
/// - Consulta de estadísticas de almacenamiento
///
/// Usa URLSession con async/await para operaciones de red, y maneja errores
/// de forma robusta con tipos de error personalizados.
///
/// # Example
/// ```swift
/// let client = VPSAPIClient.shared
///
/// // Solicitar descarga
/// do {
///     let result = try await client.downloadChapter(
///         mangaSlug: "one-piece",
///         chapterNumber: 1,
///         imageUrls: ["https://example.com/page1.jpg"]
///     )
///     print("Download success: \(result.success)")
/// } catch {
///     print("Error: \(error)")
/// }
///
/// // Verificar si está descargado
/// if let manifest = try await client.checkChapterDownloaded(
///     mangaSlug: "one-piece",
///     chapterNumber: 1
/// ) {
///     print("Chapter downloaded with \(manifest.totalPages) pages")
/// }
/// ```
@MainActor
class VPSAPIClient: ObservableObject {
    // MARK: - Singleton

    /// Instancia compartida del cliente (Singleton pattern)
    static let shared = VPSAPIClient()

    // MARK: - Properties

    /// URLSession configurada para requests HTTP
    private let session: URLSession

    /// Cola para serializar requests y evitar condiciones de carrera
    private let requestQueue = DispatchQueue(label: "com.mangareader.vpsapi", qos: .userInitiated)

    /// Token de autenticación opcional
    private var authToken: String?

    /// Published download progress tracking
    @Published var downloadProgress: [String: Double] = [:]
    @Published var activeDownloads: Set<String> = []

    // MARK: - Initialization

    /// Inicializador privado para implementar Singleton.
    ///
    /// Configura URLSession con timeouts apropiados según el tipo de request.
    private init() {
        let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = APIConfig.defaultTimeout
        configuration.timeoutIntervalForResource = APIConfig.downloadTimeout
        configuration.httpShouldSetCookies = false
        configuration.httpRequestCachePolicy = .reloadIgnoringLocalCacheData

        self.session = URLSession(configuration: configuration)
    }

    // MARK: - Authentication

    /// Configura el token de autenticación para todas las requests.
    ///
    /// - Parameter token: Token de autenticación (API key, JWT, etc.)
    ///
    /// # Example
    /// ```swift
    /// client.setAuthToken("your-api-key-or-jwt-token")
    /// ```
    func setAuthToken(_ token: String) {
        authToken = token
    }

    /// Elimina el token de autenticación.
    func clearAuthToken() {
        authToken = nil
    }

    // MARK: - Health Check

    /// Verifica si el servidor VPS está accesible.
    ///
    /// - Returns: `true` si el servidor responde correctamente, `false` en caso contrario
    ///
    /// # Example
    /// ```swift
    /// let isHealthy = try await client.checkServerHealth()
    /// if isHealthy {
    ///     print("El servidor está funcionando")
    /// } else {
    ///     print("El servidor no está accesible")
    /// }
    /// ```
    func checkServerHealth() async throws -> Bool {
        let endpoint = APIConfig.Endpoints.health()

        guard let url = URL(string: endpoint) else {
            throw VPSAPIError.invalidURL(endpoint)
        }

        let (data, _) = try await session.data(from: url)

        if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
           let status = json["status"] as? String {
            return status == "ok" || status == "healthy"
        }

        return false
    }

    // MARK: - Download Management

    /// Solicita la descarga de un capítulo al VPS.
    ///
    /// Este método inicia el proceso de descarga en el backend. El servidor
    /// descargará las imágenes desde las URLs proporcionadas y las almacenará
    /// en el VPS.
    ///
    /// - Parameters:
    ///   - mangaSlug: Slug del manga a descargar
    ///   - chapterNumber: Número del capítulo
    ///   - imageUrls: Array de URLs de las imágenes a descargar
    /// - Returns: `VPSDownloadResult` con información sobre la descarga
    /// - Throws: `VPSAPIError` si la request falla
    ///
    /// # Example
    /// ```swift
    /// do {
    ///     let result = try await client.downloadChapter(
    ///         mangaSlug: "one-piece",
    ///         chapterNumber: 1,
    ///         imageUrls: [
    ///             "https://example.com/page1.jpg",
    ///             "https://example.com/page2.jpg"
    ///         ]
    ///     )
    ///     print("Success: \(result.success)")
    ///     if let manifest = result.manifest {
    ///         print("Pages: \(manifest.totalPages)")
    ///     }
    /// } catch VPSAPIError.chapterAlreadyDownloaded {
    ///     print("El capítulo ya está descargado")
    /// } catch {
    ///     print("Error: \(error.localizedDescription)")
    /// }
    /// ```
    func downloadChapter(
        mangaSlug: String,
        chapterNumber: Int,
        imageUrls: [String]
    ) async throws -> VPSDownloadResult {
        let downloadId = "\(mangaSlug)-\(chapterNumber)"
        activeDownloads.insert(downloadId)
        downloadProgress[downloadId] = 0.0

        defer {
            activeDownloads.remove(downloadId)
            downloadProgress.removeValue(forKey: downloadId)
        }

        let endpoint = APIConfig.Endpoints.download(mangaSlug: mangaSlug, chapterNumber: chapterNumber)

        guard let url = URL(string: endpoint) else {
            throw VPSAPIError.invalidURL(endpoint)
        }

        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")

        // Agregar headers de autenticación si existen
        if let token = authToken {
            request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
        }

        let requestBody: [String: Any] = [
            "mangaSlug": mangaSlug,
            "chapterNumber": chapterNumber,
            "imageUrls": imageUrls
        ]

        request.httpBody = try JSONSerialization.data(withJSONObject: requestBody)

        // Actualizar progreso simulado (en producción, usar progress delegates)
        for i in 1...5 {
            try await Task.sleep(nanoseconds: 200_000_000) // 0.2 segundos
            downloadProgress[downloadId] = Double(i) * 0.2
        }

        let (data, response) = try await session.data(for: request)

        guard let httpResponse = response as? HTTPURLResponse else {
            throw VPSAPIError.invalidResponse
        }

        guard httpResponse.statusCode == 200 else {
            throw try mapHTTPError(statusCode: httpResponse.statusCode, data: data)
        }

        let decoder = JSONDecoder()
        let apiResponse = try decoder.decode(VPSDownloadResponse.self, from: data)

        downloadProgress[downloadId] = 1.0

        return VPSDownloadResult(
            success: apiResponse.success,
            alreadyDownloaded: apiResponse.alreadyDownloaded ?? false,
            manifest: apiResponse.manifest,
            downloaded: apiResponse.downloaded,
            failed: apiResponse.failed
        )
    }

    // MARK: - Check Chapter Status

    /// Verifica si un capítulo está descargado en el VPS.
    ///
    /// - Parameters:
    ///   - mangaSlug: Slug del manga
    ///   - chapterNumber: Número del capítulo
    /// - Returns: `VPSChapterManifest` si el capítulo existe, `nil` en caso contrario
    /// - Throws: `VPSAPIError` si hay un error de red o el servidor responde con error
    ///
    /// # Example
    /// ```swift
    /// do {
    ///     if let manifest = try await client.checkChapterDownloaded(
    ///         mangaSlug: "one-piece",
    ///         chapterNumber: 1
    ///     ) {
    ///         print("Capítulo descargado:")
    ///         print("- Páginas: \(manifest.totalPages)")
    ///         print("- Tamaño: \(manifest.totalSizeMB) MB")
    ///     } else {
    ///         print("El capítulo no está descargado")
    ///     }
    /// } catch {
    ///     print("Error verificando descarga: \(error)")
    /// }
    /// ```
    func checkChapterDownloaded(
        mangaSlug: String,
        chapterNumber: Int
    ) async throws -> VPSChapterManifest? {
        let endpoint = APIConfig.Endpoints.checkDownloaded(mangaSlug: mangaSlug, chapterNumber: chapterNumber)

        guard let url = URL(string: endpoint) else {
            throw VPSAPIError.invalidURL(endpoint)
        }

        var request = URLRequest(url: url)
        request.httpMethod = "GET"

        if let token = authToken {
            request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
        }

        let (data, response) = try await session.data(for: request)

        guard let httpResponse = response as? HTTPURLResponse else {
            throw VPSAPIError.invalidResponse
        }

        if httpResponse.statusCode == 404 {
            return nil
        }

        guard httpResponse.statusCode == 200 else {
            throw try mapHTTPError(statusCode: httpResponse.statusCode, data: data)
        }

        let decoder = JSONDecoder()
        return try decoder.decode(VPSChapterManifest.self, from: data)
    }

    /// Obtiene la lista de capítulos descargados para un manga.
    ///
    /// - Parameter mangaSlug: Slug del manga
    /// - Returns: Array de `VPSChapterInfo` con los capítulos disponibles
    /// - Throws: `VPSAPIError` si hay un error de red o el servidor responde con error
    ///
    /// # Example
    /// ```swift
    /// do {
    ///     let chapters = try await client.listDownloadedChapters(mangaSlug: "one-piece")
    ///     print("Capítulos disponibles: \(chapters.count)")
    ///     for chapter in chapters {
    ///         print("- Capítulo \(chapter.chapterNumber): \(chapter.totalPages) páginas")
    ///     }
    /// } catch {
    ///     print("Error obteniendo lista: \(error)")
    /// }
    /// ```
    func listDownloadedChapters(mangaSlug: String) async throws -> [VPSChapterInfo] {
        let endpoint = APIConfig.Endpoints.listChapters(mangaSlug: mangaSlug)

        guard let url = URL(string: endpoint) else {
            throw VPSAPIError.invalidURL(endpoint)
        }

        var request = URLRequest(url: url)
        request.httpMethod = "GET"

        if let token = authToken {
            request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
        }

        let (data, response) = try await session.data(for: request)

        guard let httpResponse = response as? HTTPURLResponse else {
            throw VPSAPIError.invalidResponse
        }

        guard httpResponse.statusCode == 200 else {
            throw try mapHTTPError(statusCode: httpResponse.statusCode, data: data)
        }

        let decoder = JSONDecoder()
        let responseObj = try decoder.decode(VPSChaptersListResponse.self, from: data)
        return responseObj.chapters
    }

    // MARK: - Image Retrieval

    /// Obtiene la URL de una imagen específica de un capítulo.
    ///
    /// Este método retorna la URL directa para acceder a una imagen almacenada
    /// en el VPS. La URL puede usarse directamente para cargar la imagen.
    ///
    /// - Parameters:
    ///   - mangaSlug: Slug del manga
    ///   - chapterNumber: Número del capítulo
    ///   - pageIndex: Índice de la página (1-based)
    /// - Returns: String con la URL completa de la imagen
    ///
    /// # Example
    /// ```swift
    /// let imageURL = client.getChapterImage(
    ///     mangaSlug: "one-piece",
    ///     chapterNumber: 1,
    ///     pageIndex: 1
    /// )
    /// print("Imagen URL: \(imageURL)")
    /// // Usar la URL para cargar la imagen en AsyncImage o SDWebImage
    /// ```
    func getChapterImage(
        mangaSlug: String,
        chapterNumber: Int,
        pageIndex: Int
    ) -> String {
        let endpoint = APIConfig.Endpoints.getImage(
            mangaSlug: mangaSlug,
            chapterNumber: chapterNumber,
            pageIndex: pageIndex
        )
        return endpoint
    }

    /// Obtiene URLs de múltiples imágenes de un capítulo.
    ///
    /// Método de conveniencia para obtener URLs para múltiples páginas.
    ///
    /// - Parameters:
    ///   - mangaSlug: Slug del manga
    ///   - chapterNumber: Número del capítulo
    ///   - pageIndices: Array de índices de página (1-based)
    /// - Returns: Array de Strings con las URLs de las imágenes
    ///
    /// # Example
    /// ```swift
    /// let imageURLs = client.getChapterImages(
    ///     mangaSlug: "one-piece",
    ///     chapterNumber: 1,
    ///     pageIndices: [1, 2, 3, 4, 5]
    /// )
    /// print("Obtenidas \(imageURLs.count) URLs")
    /// ```
    func getChapterImages(
        mangaSlug: String,
        chapterNumber: Int,
        pageIndices: [Int]
    ) -> [String] {
        return pageIndices.map { index in
            getChapterImage(
                mangaSlug: mangaSlug,
                chapterNumber: chapterNumber,
                pageIndex: index
            )
        }
    }

    // MARK: - Chapter Management

    /// Elimina un capítulo del almacenamiento del VPS.
    ///
    /// Este método elimina todas las imágenes y metadata del capítulo
    /// del servidor VPS, liberando espacio.
    ///
    /// - Parameters:
    ///   - mangaSlug: Slug del manga
    ///   - chapterNumber: Número del capítulo a eliminar
    /// - Returns: `true` si la eliminación fue exitosa, `false` en caso contrario
    /// - Throws: `VPSAPIError` si el capítulo no existe o hay un error
    ///
    /// # Example
    /// ```swift
    /// do {
    ///     let success = try await client.deleteChapterFromVPS(
    ///         mangaSlug: "one-piece",
    ///         chapterNumber: 1
    ///     )
    ///     if success {
    ///         print("Capítulo eliminado exitosamente")
    ///     } else {
    ///         print("No se pudo eliminar el capítulo")
    ///     }
    /// } catch VPSAPIError.chapterNotFound {
    ///     print("El capítulo no existía")
    /// } catch {
    ///     print("Error eliminando: \(error)")
    /// }
    /// ```
    func deleteChapterFromVPS(
        mangaSlug: String,
        chapterNumber: Int
    ) async throws -> Bool {
        let endpoint = APIConfig.Endpoints.deleteChapter(
            mangaSlug: mangaSlug,
            chapterNumber: chapterNumber
        )

        guard let url = URL(string: endpoint) else {
            throw VPSAPIError.invalidURL(endpoint)
        }

        var request = URLRequest(url: url)
        request.httpMethod = "DELETE"

        if let token = authToken {
            request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
        }

        let (data, response) = try await session.data(for: request)

        guard let httpResponse = response as? HTTPURLResponse else {
            throw VPSAPIError.invalidResponse
        }

        if httpResponse.statusCode == 404 {
            throw VPSAPIError.chapterNotFound
        }

        guard httpResponse.statusCode == 200 else {
            throw try mapHTTPError(statusCode: httpResponse.statusCode, data: data)
        }

        return true
    }

    // MARK: - Storage Statistics

    /// Obtiene estadísticas de almacenamiento del VPS.
    ///
    /// Retorna información sobre el espacio usado, disponible, total,
    /// y número de capítulos e imágenes almacenadas.
    ///
    /// - Returns: `VPSStorageStats` con todas las estadísticas
    /// - Throws: `VPSAPIError` si hay un error de red o el servidor responde con error
    ///
    /// # Example
    /// ```swift
    /// do {
    ///     let stats = try await client.getStorageStats()
    ///     print("Usado: \(stats.totalSizeFormatted)")
    ///     print("Mangas: \(stats.totalMangas)")
    ///     print("Capítulos: \(stats.totalChapters)")
    /// } catch {
    ///     print("Error obteniendo estadísticas: \(error)")
    /// }
    /// ```
    func getStorageStats() async throws -> VPSStorageStats {
        let endpoint = APIConfig.Endpoints.storageStats()

        guard let url = URL(string: endpoint) else {
            throw VPSAPIError.invalidURL(endpoint)
        }

        var request = URLRequest(url: url)
        request.httpMethod = "GET"

        if let token = authToken {
            request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
        }

        let (data, response) = try await session.data(for: request)

        guard let httpResponse = response as? HTTPURLResponse else {
            throw VPSAPIError.invalidResponse
        }

        guard httpResponse.statusCode == 200 else {
            throw try mapHTTPError(statusCode: httpResponse.statusCode, data: data)
        }

        let decoder = JSONDecoder()
        return try decoder.decode(VPSStorageStats.self, from: data)
    }

    // MARK: - Private Methods

    /// Mapea un código de error HTTP a un `VPSAPIError` específico.
    ///
    /// - Parameters:
    ///   - statusCode: Código de estado HTTP
    ///   - data: Datos de la respuesta (puede contener mensaje de error)
    /// - Returns: `VPSAPIError` apropiado
    /// - Throws: Error de decodificación si no puede leer el mensaje de error
    private func mapHTTPError(statusCode: Int, data: Data) throws -> VPSAPIError {
        // Intentar leer mensaje de error del cuerpo de la respuesta
        let errorMessage: String?
        if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
           let message = json["message"] as? String {
            errorMessage = message
        } else {
            errorMessage = nil
        }

        switch statusCode {
        case 400:
            return .badRequest(errorMessage ?? "Bad request")
        case 401:
            return .unauthorized
        case 403:
            return .forbidden
        case 404:
            return .chapterNotFound
        case 409:
            return .chapterAlreadyDownloaded
        case 422:
            return .invalidImageFormat(errorMessage ?? "Invalid image format")
        case 429:
            return .rateLimited
        case 500:
            return .serverError(errorMessage ?? "Internal server error")
        case 503:
            return .serviceUnavailable
        case 507:
            return .storageLimitExceeded
        default:
            return .httpError(statusCode: statusCode)
        }
    }
}

// MARK: - Error Types

/// Errores específicos del cliente de API VPS.
enum VPSAPIError: LocalizedError {
    case invalidURL(String)
    case invalidResponse
    case httpError(statusCode: Int)
    case networkError(Error)
    case decodingError(Error)
    case encodingError(Error)
    case badRequest(String)
    case unauthorized
    case forbidden
    case chapterNotFound
    case chapterAlreadyDownloaded
    case imageNotFound
    case invalidImageFormat(String)
    case rateLimited
    case storageLimitExceeded
    case serverError(String)
    case serviceUnavailable

    var errorDescription: String? {
        switch self {
        case .invalidURL(let url):
            return "URL inválida: \(url)"
        case .invalidResponse:
            return "Respuesta inválida del servidor"
        case .httpError(let statusCode):
            return "Error HTTP \(statusCode)"
        case .networkError(let error):
            return "Error de red: \(error.localizedDescription)"
        case .decodingError(let error):
            return "Error al decodificar respuesta: \(error.localizedDescription)"
        case .encodingError(let error):
            return "Error al codificar solicitud: \(error.localizedDescription)"
        case .badRequest(let message):
            return "Solicitud inválida: \(message)"
        case .unauthorized:
            return "No autorizado: credenciales inválidas o expiradas"
        case .forbidden:
            return "Acceso prohibido"
        case .chapterNotFound:
            return "El capítulo no existe en el VPS"
        case .chapterAlreadyDownloaded:
            return "El capítulo ya está descargado en el VPS"
        case .imageNotFound:
            return "La imagen solicitada no existe"
        case .invalidImageFormat(let message):
            return "Formato de imagen inválido: \(message)"
        case .rateLimited:
            return "Demasiadas solicitudes. Intente más tarde."
        case .storageLimitExceeded:
            return "Límite de almacenamiento excedido"
        case .serverError(let message):
            return "Error del servidor: \(message)"
        case .serviceUnavailable:
            return "Servicio no disponible temporalmente"
        }
    }

    var recoverySuggestion: String? {
        switch self {
        case .unauthorized:
            return "Verifique sus credenciales o actualice el token de autenticación"
        case .rateLimited:
            return "Espere unos minutos antes de intentar nuevamente"
        case .storageLimitExceeded:
            return "Elimine algunos capítulos antiguos para liberar espacio"
        case .serviceUnavailable:
            return "Intente nuevamente en unos minutos"
        case .networkError:
            return "Verifique su conexión a internet"
        default:
            return nil
        }
    }
}

// MARK: - Response Models

struct VPSDownloadResult {
    let success: Bool
    let alreadyDownloaded: Bool
    let manifest: VPSChapterManifest?
    let downloaded: Int?
    let failed: Int?
}

struct VPSDownloadResponse: Codable {
    let success: Bool
    let alreadyDownloaded: Bool?
    let manifest: VPSChapterManifest?
    let downloaded: Int?
    let failed: Int?
}

struct VPSChapterManifest: Codable {
    let mangaSlug: String
    let chapterNumber: Int
    let totalPages: Int
    let downloadedPages: Int
    let failedPages: Int
    let downloadDate: String
    let totalSize: Int
    let images: [VPSImageInfo]

    var totalSizeMB: String {
        String(format: "%.2f", Double(totalSize) / 1024 / 1024)
    }
}

struct VPSImageInfo: Codable {
    let page: Int
    let filename: String
    let url: String
    let size: Int

    var sizeKB: String {
        String(format: "%.2f", Double(size) / 1024)
    }
}

struct VPSChapterInfo: Codable {
    let chapterNumber: Int
    let downloadDate: String
    let totalPages: Int
    let downloadedPages: Int
    let totalSize: Int
    let totalSizeMB: String
}

struct VPSChaptersListResponse: Codable {
    let mangaSlug: String
    let totalChapters: Int
    let chapters: [VPSChapterInfo]
}

struct VPSStorageStats: Codable {
    let totalMangas: Int
    let totalChapters: Int
    let totalSize: Int
    let totalSizeMB: String
    let totalSizeFormatted: String
    let mangaDetails: [VPSMangaDetail]

    struct VPSMangaDetail: Codable {
        let mangaSlug: String
        let chapters: Int
        let totalSize: Int
        let totalSizeMB: String
    }
}
