package com.example.boidelov3;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

/**
 * Service pour générer des questions via différentes IA (OpenAI, OpenRouter, Z.ai)
 * Utilise OkHttp pour les requêtes réseau asynchrones
 */
public class OpenAIService {
    private static final String TAG = "OpenAIService";
    private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");

    // API URLs pour chaque provider
    private static final String OPENAI_API_URL = "https://api.openai.com/v1/chat/completions";
    private static final String OPENROUTER_API_URL = "https://openrouter.ai/api/v1/chat/completions";

    // Modèles par défaut pour chaque provider
    private static final String OPENAI_MODEL = "gpt-3.5-turbo";
    private static final String OPENROUTER_MODEL = "openai/gpt-3.5-turbo";
    private static final String ZAI_MODEL = "claude-3-5-sonnet";

    private OkHttpClient client;
    private String apiKey;
    private AIProvider provider;
    private String model;
    private Handler mainHandler;
    private Random random;

    /**
     * Enum pour les différents fournisseurs d'IA
     */
    public enum AIProvider {
        OPENAI("OpenAI", OPENAI_API_URL, OPENAI_MODEL),
        OPENROUTER("OpenRouter", OPENROUTER_API_URL, OPENROUTER_MODEL),
        ZAI("Z.ai", "https://api.z.ai/v1/messages", ZAI_MODEL);

        private final String displayName;
        private final String apiUrl;
        private final String defaultModel;

        AIProvider(String displayName, String apiUrl, String defaultModel) {
            this.displayName = displayName;
            this.apiUrl = apiUrl;
            this.defaultModel = defaultModel;
        }

        public String getDisplayName() {
            return displayName;
        }

        public String getApiUrl() {
            return apiUrl;
        }

        public String getDefaultModel() {
            return defaultModel;
        }
    }

    /**
     * Constructeur avec provider par défaut (OpenAI)
     */
    public OpenAIService(String apiKey) {
        this(apiKey, AIProvider.OPENAI);
    }

    /**
     * Constructeur avec choix du provider
     * Valide la clé API avant de l'utiliser
     */
    public OpenAIService(String apiKey, AIProvider provider) {
        // Valider la clé API avant utilisation
        if (!validateApiKey(apiKey, provider)) {
            throw new IllegalArgumentException("Clé API invalide pour " + provider.getDisplayName());
        }

        this.apiKey = apiKey;
        this.provider = provider;
        this.model = provider.getDefaultModel();
        this.client = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .build();
        this.mainHandler = new Handler(Looper.getMainLooper());
        this.random = new Random();
    }

    /**
     * Constructeur avec choix du provider et du modèle
     * Valide la clé API avant de l'utiliser
     */
    public OpenAIService(String apiKey, AIProvider provider, String model) {
        this(apiKey, provider);
        this.model = model;
    }

    /**
     * Valide une clé API selon le provider
     * Vérifie que la clé n'est pas null, vide, et a un format valide
     *
     * @param key La clé API à valider
     * @param provider Le provider d'IA
     * @return true si la clé est valide, false sinon
     */
    public static boolean validateApiKey(String key, AIProvider provider) {
        // Vérification basique : la clé ne doit pas être null ou vide
        if (key == null || key.trim().isEmpty()) {
            Log.w(TAG, "Clé API null ou vide pour " + provider.getDisplayName());
            return false;
        }

        String trimmedKey = key.trim();

        // Vérification du format selon le provider
        switch (provider) {
            case OPENAI:
                // Les clés OpenAI commencent par "sk-"
                if (!trimmedKey.startsWith("sk-")) {
                    Log.w(TAG, "Clé OpenAI invalide : doit commencer par 'sk-'");
                    return false;
                }
                if (trimmedKey.length() < 20) {
                    Log.w(TAG, "Clé OpenAI trop courte");
                    return false;
                }
                break;

            case OPENROUTER:
                // Les clés OpenRouter commencent par "sk-or-"
                if (!trimmedKey.startsWith("sk-or-")) {
                    Log.w(TAG, "Clé OpenRouter invalide : doit commencer par 'sk-or-'");
                    return false;
                }
                if (trimmedKey.length() < 20) {
                    Log.w(TAG, "Clé OpenRouter trop courte");
                    return false;
                }
                break;

            case ZAI:
                // Les clés Z.ai/Anthropic commencent par "sk-ant-"
                if (!trimmedKey.startsWith("sk-ant-")) {
                    Log.w(TAG, "Clé Z.ai invalide : doit commencer par 'sk-ant-'");
                    return false;
                }
                if (trimmedKey.length() < 20) {
                    Log.w(TAG, "Clé Z.ai trop courte");
                    return false;
                }
                break;

            default:
                Log.w(TAG, "Provider inconnu : " + provider);
                return false;
        }

        Log.i(TAG, "Clé API validée pour " + provider.getDisplayName());
        return true;
    }

    /**
     * Valide une clé API avec le provider par défaut (OpenAI)
     *
     * @param key La clé API à valider
     * @return true si la clé est valide
     */
    public static boolean validateApiKey(String key) {
        return validateApiKey(key, AIProvider.OPENAI);
    }

    /**
     * Change le provider
     */
    public void setProvider(AIProvider provider) {
        this.provider = provider;
        this.model = provider.getDefaultModel();
    }

    /**
     * Change le modèle
     */
    public void setModel(String model) {
        this.model = model;
    }

    /**
     * Génère une question via l'API configurée
     */
    public void generateQuestion(List<String> players, int ajoutGorgees, OpenAICallback callback) {
        String prompt = buildPrompt(players, ajoutGorgees);

        try {
            boolean isAnthropicFormat = (provider == AIProvider.ZAI);
            String requestBody;
            String responseParser;

            if (isAnthropicFormat) {
                // Format Anthropic (Claude/Z.ai)
                JSONObject body = new JSONObject();
                body.put("model", model);
                body.put("max_tokens", 150);
                body.put("temperature", 0.8);

                JSONArray messages = new JSONArray();
                JSONObject userMessage = new JSONObject();
                userMessage.put("role", "user");
                userMessage.put("content", prompt);
                messages.put(userMessage);
                body.put("messages", messages);

                requestBody = body.toString();
                responseParser = "anthropic";
            } else {
                // Format OpenAI-compatible
                JSONObject body = new JSONObject();
                body.put("model", model);
                body.put("max_tokens", 150);
                body.put("temperature", 0.8);

                JSONArray messages = new JSONArray();
                JSONObject userMessage = new JSONObject();
                userMessage.put("role", "user");
                userMessage.put("content", prompt);
                messages.put(userMessage);
                body.put("messages", messages);

                requestBody = body.toString();
                responseParser = "openai";
            }

            // Construire la requête avec l'URL appropriée
            Request.Builder requestBuilder = new Request.Builder()
                    .url(provider.getApiUrl())
                    .post(RequestBody.create(requestBody, JSON));

            // Ajouter les headers selon le provider
            switch (provider) {
                case OPENAI:
                    requestBuilder.addHeader("Authorization", "Bearer " + apiKey);
                    break;
                case OPENROUTER:
                    requestBuilder.addHeader("Authorization", "Bearer " + apiKey);
                    requestBuilder.addHeader("HTTP-Referer", "https://boidelo.app");
                    requestBuilder.addHeader("X-Title", "Boidelo");
                    break;
                case ZAI:
                    requestBuilder.addHeader("x-api-key", apiKey);
                    requestBuilder.addHeader("anthropic-version", "2023-06-01");
                    break;
            }

            Request request = requestBuilder.build();

            client.newCall(request).enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    mainHandler.post(() -> callback.onError("Erreur de connexion: " + e.getMessage()));
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    try {
                        if (!response.isSuccessful()) {
                            mainHandler.post(() -> callback.onError("Erreur API " + provider.getDisplayName() + ": " + response.code()));
                            return;
                        }

                        String responseData = response.body().string();
                        String generatedQuestion = parseResponse(responseData, responseParser);

                        mainHandler.post(() -> callback.onSuccess(generatedQuestion));
                    } finally {
                        response.close();
                    }
                }
            });

        } catch (JSONException e) {
            mainHandler.post(() -> callback.onError("Erreur de formatage: " + e.getMessage()));
        }
    }

    /**
     * Construit le prompt pour l'IA
     */
    private String buildPrompt(List<String> players, int ajoutGorgees) {
        StringBuilder sb = new StringBuilder();
        sb.append("Tu es un générateur de questions pour un jeu d'alcool en français. ");
        sb.append("Génère UNE SEULE question dans le style suivant:\n\n");

        sb.append("Exemples:\n");
        sb.append("- Ceux qui ont un tatouage\n");
        sb.append("- Le/La plus drôle du groupe\n");
        sb.append("- <J1> doit deviner ce que <J2> a mangé aujourd'hui\n");
        sb.append("- Ceux qui écoutent du rock\n");
        sb.append("- Plutôt avoir le pouvoir ou la connaissance ? Votez tous. La minorité boit.\n");

        if (players != null && !players.isEmpty()) {
            sb.append("\nJoueurs présents: ");
            for (int i = 0; i < players.size(); i++) {
                if (i > 0) sb.append(", ");
                sb.append(players.get(i));
            }
        }

        if (ajoutGorgees > 0) {
            sb.append("\nAjout de gorgées: +").append(ajoutGorgees).append(" gorgée(s)");
        }

        sb.append("\n\nIMPORTANT: ");
        sb.append("Génère uniquement la question, sans explication. ");
        sb.append("Utilise <J1>, <J2>, <J3> pour désigner des joueurs aléatoires si nécessaire. ");
        sb.append("Ne génère PAS de questions avec <manches> (défis à durée). ");
        sb.append("Sois créatif et varié !");

        return sb.toString();
    }

    /**
     * Parse la réponse de l'API (format OpenAI-compatible ou Anthropic)
     */
    private String parseResponse(String responseData, String format) {
        try {
            JSONObject json = new JSONObject(responseData);

            if ("anthropic".equals(format)) {
                // Format Anthropic/Z.ai
                if (json.has("content")) {
                    JSONArray contentArray = json.getJSONArray("content");
                    if (contentArray.length() > 0) {
                        JSONObject firstContent = contentArray.getJSONObject(0);
                        if (firstContent.has("text")) {
                            return firstContent.getString("text").trim();
                        }
                    }
                }
            } else {
                // Format OpenAI-compatible
                JSONArray choices = json.getJSONArray("choices");
                if (choices.length() > 0) {
                    JSONObject firstChoice = choices.getJSONObject(0);
                    JSONObject message = firstChoice.getJSONObject("message");
                    String content = message.getString("content");
                    // Nettoyer la réponse
                    return content.trim().replaceAll("^\"|\"$", "");
                }
            }
        } catch (JSONException e) {
            String operation = "Parsing de la réponse API " + provider.getDisplayName();
            String details = "Format: " + format + ", Impossible de parser la réponse JSON";
            Log.e("OpenAIService", operation + " - " + details, e);
        }
        return null;
    }

    /**
     * Interface de callback pour les réponses API
     */
    public interface OpenAICallback {
        void onSuccess(String question);
        void onError(String errorMessage);
    }

    /**
     * Libère les ressources
     */
    public void shutdown() {
        if (client != null) {
            client.dispatcher().executorService().shutdown();
            client.connectionPool().evictAll();
        }
    }
}
