import CoreLocation
import Foundation

enum TrackerError: LocalizedError {
    case message(String)
    var errorDescription: String? { if case let .message(value) = self { return value }; return "Erreur FilyX" }
}

struct PairResponse: Decodable { let device_token: String; let channel: ChannelInfo }
struct ChannelInfo: Decodable { let slug: String; let name: String }
struct TripInfo: Decodable { let id: Int; let name: String; let status: String; let mode: String }
struct TrackerState: Decodable { let tracking_active: Bool; let channel: ChannelInfo; let trip: TripInfo?; let upload_interval_seconds: Int }
private struct ErrorResponse: Decodable { let error: String? }

actor APIClient {
    static let shared = APIClient()
    private let baseURL = URL(string: "https://filyx.hom3r-off.fr")!

    func pair(code: String, deviceName: String) async throws -> PairResponse {
        try await request(path: "/api/irl/mobile/pair", method: "POST", body: ["code": code, "device_name": deviceName, "platform": "ios"], token: "")
    }

    func state() async throws -> TrackerState {
        try await request(path: "/api/irl/mobile/state", method: "GET", body: nil, token: KeychainStore.read())
    }

    func send(_ location: CLLocation) async throws {
        let body: [String: Any] = ["latitude": location.coordinate.latitude, "longitude": location.coordinate.longitude, "accuracy": location.horizontalAccuracy, "altitude": location.altitude, "speed": max(0, location.speed), "heading": max(0, location.course), "timestamp": Int(location.timestamp.timeIntervalSince1970 * 1000)]
        let _: EmptyResponse = try await request(path: "/api/irl/mobile/location", method: "POST", body: body, token: KeychainStore.read())
    }

    private func request<T: Decodable>(path: String, method: String, body: [String: Any]?, token: String) async throws -> T {
        guard let endpoint = URL(string: path, relativeTo: baseURL) else { throw TrackerError.message("Adresse FilyX invalide.") }
        var request = URLRequest(url: endpoint); request.httpMethod = method; request.timeoutInterval = 18
        request.setValue("application/json", forHTTPHeaderField: "Accept")
        if !token.isEmpty { request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") }
        if let body { request.httpBody = try JSONSerialization.data(withJSONObject: body); request.setValue("application/json", forHTTPHeaderField: "Content-Type") }
        let (data, response) = try await URLSession.shared.data(for: request)
        guard let http = response as? HTTPURLResponse, 200..<300 ~= http.statusCode else {
            let error = try? JSONDecoder().decode(ErrorResponse.self, from: data); throw TrackerError.message(error?.error ?? "FilyX est temporairement indisponible.")
        }
        return try JSONDecoder().decode(T.self, from: data)
    }
}

private struct EmptyResponse: Decodable { let ok: Bool }
