import Foundation

protocol APIClientProtocol {
    func get<T: Decodable>(path: String, queryItems: [URLQueryItem], token: String?) async throws -> T
    func post<T: Decodable, Body: Encodable>(path: String, body: Body, token: String?) async throws -> T
}

enum APIError: LocalizedError {
    case invalidURL
    case invalidResponse
    case unauthorized
    case server(status: Int, message: String)
    case decoding(Error)
    case transport(Error)

    var errorDescription: String? {
        switch self {
        case .invalidURL: return "Geçersiz URL"
        case .invalidResponse: return "Geçersiz sunucu yanıtı"
        case .unauthorized: return "Oturum süresi doldu"
        case .server(_, let message): return message
        case .decoding: return "Sunucu verisi çözümlenemedi"
        case .transport(let error): return error.localizedDescription
        }
    }
}

final class APIClient: APIClientProtocol {
    private let baseURL: URL
    private let session: URLSession
    private let decoder: JSONDecoder
    private let encoder: JSONEncoder

    init(baseURL: URL, session: URLSession = .shared) {
        self.baseURL = baseURL
        self.session = session
        self.decoder = JSONDecoder()
        self.encoder = JSONEncoder()
    }

    func get<T: Decodable>(path: String, queryItems: [URLQueryItem] = [], token: String? = nil) async throws -> T {
        var request = try buildRequest(path: path, method: "GET", queryItems: queryItems, token: token)
        request.httpBody = nil
        return try await perform(request)
    }

    func post<T: Decodable, Body: Encodable>(path: String, body: Body, token: String? = nil) async throws -> T {
        var request = try buildRequest(path: path, method: "POST", queryItems: [], token: token)
        request.httpBody = try encoder.encode(body)
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        return try await perform(request)
    }

    private func buildRequest(path: String, method: String, queryItems: [URLQueryItem], token: String?) throws -> URLRequest {
        guard var components = URLComponents(url: baseURL.appendingPathComponent(path), resolvingAgainstBaseURL: false) else {
            throw APIError.invalidURL
        }
        if !queryItems.isEmpty {
            components.queryItems = queryItems
        }
        guard let url = components.url else { throw APIError.invalidURL }

        var request = URLRequest(url: url)
        request.httpMethod = method
        request.timeoutInterval = 20
        if let token {
            request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
        }
        return request
    }

    private func perform<T: Decodable>(_ request: URLRequest) async throws -> T {
        #if DEBUG
        print("[API] \(request.httpMethod ?? "") \(request.url?.absoluteString ?? "")")
        #endif

        do {
            let (data, response) = try await session.data(for: request)
            guard let http = response as? HTTPURLResponse else { throw APIError.invalidResponse }

            if http.statusCode == 401 { throw APIError.unauthorized }

            guard (200...299).contains(http.statusCode) else {
                let message = (try? JSONSerialization.jsonObject(with: data) as? [String: Any])?["message"] as? String ?? "Sunucu hatası"
                throw APIError.server(status: http.statusCode, message: message)
            }

            do {
                return try decoder.decode(T.self, from: data)
            } catch {
                throw APIError.decoding(error)
            }
        } catch let error as APIError {
            throw error
        } catch {
            throw APIError.transport(error)
        }
    }
}

extension Bundle {
    var apiBaseURL: URL {
        let raw = (object(forInfoDictionaryKey: "API_BASE_URL") as? String)?
            .trimmingCharacters(in: .whitespacesAndNewlines) ?? ""

        // 1) Normal case: full URL in Info.plist / xcconfig.
        if let url = URL(string: raw), let host = url.host, !host.isEmpty {
            return url
        }

        // 2) If scheme is missing (e.g. "192.168.1.124:8080"), prepend http://.
        if !raw.isEmpty, !raw.contains("://"),
           let url = URL(string: "http://\(raw)"),
           let host = url.host, !host.isEmpty {
            return url
        }

        // 3) Device-local fallback for current dev network.
        if let fallback = URL(string: "http://192.168.1.124:8080") {
            #if DEBUG
            print("[API] Invalid API_BASE_URL='\(raw)'. Falling back to \(fallback.absoluteString)")
            #endif
            return fallback
        }

        // 4) Last resort.
        return URL(string: "http://localhost:8080")!
    }
}
