import 'dart:convert';

import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:google_generative_ai/google_generative_ai.dart';
import 'package:http/http.dart' as http;

class GeminiService {
  static final GeminiService _instance = GeminiService._internal();

  late final String _apiKey;

  /// Cache of valid model IDs (flash/lite, supporting generateContent).
  List<String> _validModels = [];

  factory GeminiService() => _instance;

  GeminiService._internal() {
    final apiKey = dotenv.env['GEMINI_API_KEY'];
    if (apiKey == null || apiKey.isEmpty) {
      throw Exception('GEMINI_API_KEY not found in .env file');
    }
    _apiKey = apiKey;
  }

  // ---------------------------------------------------------------------------
  // Model discovery
  // ---------------------------------------------------------------------------

  /// Queries the Gemini REST API for available models and caches those that
  ///  - support `generateContent`, AND
  ///  - contain "flash" or "lite" in their name (free-tier / fast models).
  ///
  /// Returns a stable cached list on subsequent calls.
  Future<List<String>> _getValidModels() async {
    if (_validModels.isNotEmpty) return _validModels;

    try {
      final uri = Uri.parse(
        'https://generativelanguage.googleapis.com/v1beta/models?key=$_apiKey',
      );
      final response = await http.get(uri);

      if (response.statusCode == 200) {
        final data = jsonDecode(response.body) as Map<String, dynamic>;
        final rawModels = (data['models'] as List<dynamic>?) ?? [];

        final discovered = <String>[];
        for (final m in rawModels) {
          final fullName = (m['name'] as String? ?? '');
          final lower = fullName.toLowerCase();
          final methods =
              (m['supportedGenerationMethods'] as List<dynamic>?) ?? [];

          if (methods.contains('generateContent') &&
              (lower.contains('flash') || lower.contains('lite'))) {
            // Strip the "models/" prefix so it can be passed directly to
            // GenerativeModel(model: ...).
            final id = fullName.startsWith('models/')
                ? fullName.substring('models/'.length)
                : fullName;
            discovered.add(id);
          }
        }

        // Sort descending so newer/more-capable models are tried first.
        discovered.sort((a, b) => b.compareTo(a));
        _validModels = discovered;
      }
    } catch (_) {
      // Fall back to hard-coded list of known free-tier models below.
    }

    // If discovery failed or returned nothing, use safe known fallbacks.
    if (_validModels.isEmpty) {
      _validModels = [
        'gemini-2.5-flash-lite',
        'gemini-2.5-flash',
        'gemini-2.0-flash',
        'gemini-1.5-flash',
      ];
    }

    return _validModels;
  }

  // ---------------------------------------------------------------------------
  // Public API
  // ---------------------------------------------------------------------------

  /// Fixes spelling/grammar, improves clarity, and translates [text] to
  /// professional English.
  ///
  /// Provide a custom [promptInstruction] to give Gemini field-specific
  /// context (subject, description, action-taken).  If omitted a sensible
  /// default is used.
  ///
  /// Automatically retries with the next available model on 429 / quota
  /// errors to minimise wasted quota calls.
  Future<String> enhanceText(String text, {String? promptInstruction}) async {
    if (text.trim().isEmpty) return text;
    final instruction =
        promptInstruction ??
        'Fix spelling and grammar, improve clarity, and translate to '
            'professional English. Return ONLY the improved text, no explanations:';
    final prompt = '$instruction\n\n"$text"';
    return _generateWithRetry(prompt, fallback: text);
  }

  // ---------------------------------------------------------------------------
  // Internal helpers
  // ---------------------------------------------------------------------------

  /// Sends [prompt] to Gemini, retrying across all valid models on 429 errors.
  /// Returns the model response on success, or throws if all models fail.
  Future<String> _generateWithRetry(
    String prompt, {
    required String fallback,
  }) async {
    final models = await _getValidModels();

    for (int i = 0; i < models.length; i++) {
      try {
        final model = GenerativeModel(model: models[i], apiKey: _apiKey);
        final response = await model.generateContent([Content.text(prompt)]);
        return response.text ?? fallback;
      } catch (e) {
        final msg = e.toString().toLowerCase();
        final is429 =
            msg.contains('429') ||
            msg.contains('quota') ||
            msg.contains('resource_exhausted');

        if (!is429 || i == models.length - 1) {
          // Non-quota error or last model — give up.
          throw Exception('Gemini request failed: $e');
        }
        // Quota hit — try the next model.
      }
    }

    // Should not reach here, but safety fallback.
    throw Exception('All Gemini models exhausted');
  }
}
