import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:votianlt_app/config/translation_config.dart';
import 'package:votianlt_app/services/dart_mq.dart';
import 'package:votianlt_app/services/developer.dart' as developer;
import 'package:votianlt_app/app_state.dart';

/// Service für Übersetzungen – unterstützt LM Studio (lokal) und Moonshot AI (Cloud).
///
/// Das aktive Backend wird in [TranslationConfig.activeBackend] konfiguriert.
/// Verwendet das Singleton-Pattern wie andere Services in der App.
/// Übersetzt in die vom Benutzer in der App eingestellte Sprache.
class TranslationService {
  static final TranslationService _instance = TranslationService._internal();
  factory TranslationService() => _instance;
  TranslationService._internal();

  static const String _chatCompletionsEndpoint = '/chat/completions';

  // HTTP Client
  final http.Client _client = http.Client();

  // Verfügbarkeitsstatus
  bool _isAvailable = false;

  // Aktuell eingestellte Zielsprache (aus der App)
  String get _targetLanguageCode => AppState().languageCode;

  /// Gibt an ob das Übersetzungsbackend verfügbar ist
  bool get isAvailable => _isAvailable;

  /// Name des aktiven Backends (für Logs und DartMQ-Nachrichten)
  String get _backendName => switch (TranslationConfig.activeBackend) {
        TranslationBackend.lmStudio => 'lm-studio',
        TranslationBackend.moonshot => 'moonshot-ai',
      };

  // Verfügbare Sprachen für Übersetzung (alle unterstützten App-Sprachen)
  static final Map<String, String> supportedLanguages = {
    'de': 'German',
    'en': 'English',
    'es': 'Spanish',
    'fr': 'French',
    'pl': 'Polish',
    'ru': 'Russian',
    'tr': 'Turkish',
    'et': 'Estonian',
    'lv': 'Latvian',
    'lt': 'Lithuanian',
  };

  /// Initialisiert den Translation Service
  Future<void> initialize() async {
    try {
      // Auf Sprachänderungen hören
      _listenToLanguageChanges();

      // Verfügbarkeit prüfen
      _isAvailable = await _checkAvailability();

      _notifyInitialization();

      developer.log(
        'TranslationService initialisiert - Backend: $_backendName, Zielsprache: ${supportedLanguages[_targetLanguageCode]}',
        name: 'TranslationService',
      );
    } catch (e) {
      developer.log('Fehler bei Initialisierung des TranslationService: $e',
          name: 'TranslationService');
      _isAvailable = false;
    }
  }

  /// Prüft ob das konfigurierte Backend erreichbar ist
  Future<bool> _checkAvailability() async {
    switch (TranslationConfig.activeBackend) {
      case TranslationBackend.lmStudio:
        return _checkLmStudioAvailability();
      case TranslationBackend.moonshot:
        return _checkMoonshotAvailability();
    }
  }

  Future<bool> _checkLmStudioAvailability() async {
    try {
      final response = await _client
          .get(Uri.parse('${TranslationConfig.lmStudioBaseUrl}/v1/models'))
          .timeout(const Duration(seconds: 5));

      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        final models = data['data'] as List<dynamic>?;
        developer.log(
          'LM Studio verbunden - Verfügbare Modelle: ${models?.length ?? 0}',
          name: 'TranslationService',
        );
        return true;
      }
      return false;
    } catch (e) {
      developer.log('LM Studio nicht erreichbar: $e', name: 'TranslationService');
      return false;
    }
  }

  Future<bool> _checkMoonshotAvailability() async {
    try {
      final response = await _client
          .get(
            Uri.parse('${TranslationConfig.moonshotBaseUrl}/models'),
            headers: {'Authorization': 'Bearer ${TranslationConfig.moonshotApiKey}'},
          )
          .timeout(const Duration(seconds: 5));

      if (response.statusCode == 200) {
        developer.log('Moonshot AI verbunden - API erreichbar', name: 'TranslationService');
        return true;
      }
      return false;
    } catch (e) {
      developer.log('Moonshot AI nicht erreichbar: $e', name: 'TranslationService');
      return false;
    }
  }

  /// Sendet Initialisierungs-Notification über DartMQ
  void _notifyInitialization() {
    DartMQ().publish<Map<String, dynamic>>('translation/service_initialized', {
      'language': _targetLanguageCode,
      'backend': _backendName,
      'isAvailable': isAvailable,
      'endpoint': _activeEndpoint,
    });
  }

  /// Hört auf Sprachänderungen und aktualisiert den Service
  void _listenToLanguageChanges() {
    localeNotifier.addListener(() {
      final newLanguage = AppState().languageCode;
      developer.log(
        'Sprache in App geändert zu: ${supportedLanguages[newLanguage] ?? newLanguage}',
        name: 'TranslationService',
      );
      DartMQ().publish<Map<String, dynamic>>('translation/language_changed', {
        'language': newLanguage,
        'displayName': supportedLanguages[newLanguage],
        'backend': _backendName,
      });
    });
  }

  /// Basis-URL des aktiven Backends
  String get _activeEndpoint => switch (TranslationConfig.activeBackend) {
        TranslationBackend.lmStudio => TranslationConfig.lmStudioBaseUrl,
        TranslationBackend.moonshot => TranslationConfig.moonshotBaseUrl,
      };

  /// Übersetzt einen Text in die vom Benutzer eingestellte Sprache
  ///
  /// [text] - Der zu übersetzende Text
  /// [sourceLanguage] - Die Ausgangssprache (optional, wird automatisch erkannt wenn null)
  ///
  /// Gibt den übersetzten Text zurück oder den Originaltext bei Fehlern
  Future<String> translate(
    String text, {
    String? sourceLanguage,
  }) async {
    if (text.isEmpty) {
      return text;
    }

    // Bei rein numerischem Text oder sehr kurzem Text nicht übersetzen
    if (_shouldSkipTranslation(text)) {
      developer.log('Übersetzung übersprungen (kein Text): "$text"',
          name: 'TranslationService');
      return text;
    }

    // Zielsprache aus der App holen
    final targetCode = _targetLanguageCode;

    // Wenn Quelle gleich Ziel, nicht übersetzen
    final detectedSource = sourceLanguage ?? await _detectLanguage(text);
    if (detectedSource == targetCode) {
      developer.log('Übersetzung übersprungen (Quelle = Ziel): "$text"',
          name: 'TranslationService');
      return text;
    }

    try {
      final translatedText = await _translate(text, detectedSource, targetCode);

      developer.log(
        'Übersetzung [${supportedLanguages[detectedSource]} -> ${supportedLanguages[targetCode]}]:\n'
        '  Original: "$text"\n'
        '  Übersetzt: "$translatedText"',
        name: 'TranslationService',
      );

      return translatedText;
    } catch (e) {
      developer.log(
        'Fehler bei der Übersetzung: $e\n  Original: "$text"',
        name: 'TranslationService',
      );
      return text; // Bei Fehler Original zurückgeben
    }
  }

  /// Übersetzt eine Liste von Texten in die vom Benutzer eingestellte Sprache
  Future<List<String>> translateList(
    List<String> texts, {
    String? sourceLanguage,
  }) async {
    if (texts.isEmpty) return texts;

    final results = <String>[];
    final targetCode = _targetLanguageCode;

    developer.log(
      'Starte Batch-Übersetzung von ${texts.length} Texten nach ${supportedLanguages[targetCode]}',
      name: 'TranslationService',
    );

    for (int i = 0; i < texts.length; i++) {
      final text = texts[i];

      if (text.isEmpty || _shouldSkipTranslation(text)) {
        results.add(text);
        continue;
      }

      try {
        final detectedSource = sourceLanguage ?? await _detectLanguage(text);

        if (detectedSource == targetCode) {
          results.add(text);
          continue;
        }

        final translatedText = await _translate(text, detectedSource, targetCode);

        developer.log(
          'Batch [${i + 1}/${texts.length}] [${supportedLanguages[detectedSource]} -> ${supportedLanguages[targetCode]}]:\n'
          '  Original: "$text"\n'
          '  Übersetzt: "$translatedText"',
          name: 'TranslationService',
        );

        results.add(translatedText);
      } catch (e) {
        developer.log(
          'Fehler bei Batch-Übersetzung [${i + 1}/${texts.length}]: $e\n  Original: "$text"',
          name: 'TranslationService',
        );
        results.add(text);
      }
    }

    developer.log(
      'Batch-Übersetzung abgeschlossen: ${texts.length} Texte verarbeitet',
      name: 'TranslationService',
    );

    return results;
  }

  /// Dispatcht die Übersetzung an das konfigurierte Backend
  Future<String> _translate(String text, String sourceCode, String targetCode) {
    switch (TranslationConfig.activeBackend) {
      case TranslationBackend.lmStudio:
        return _translateWithLmStudio(text, sourceCode, targetCode);
      case TranslationBackend.moonshot:
        return _translateWithMoonshot(text, sourceCode, targetCode);
    }
  }

  /// Übersetzung mit LM Studio REST API (lokales Modell, kein API-Key)
  Future<String> _translateWithLmStudio(
    String text,
    String sourceCode,
    String targetCode,
  ) async {
    final targetName = supportedLanguages[targetCode] ?? targetCode;
    final sourceName = supportedLanguages[sourceCode] ?? sourceCode;

    final systemPrompt =
        'You are a professional translator. Translate the user input from $sourceName to $targetName. '
        'Return ONLY the translation, without any additional text, explanations, or quotes.';

    final requestBody = {
      'model': TranslationConfig.lmStudioModel,
      'messages': [
        {'role': 'system', 'content': systemPrompt},
        {'role': 'user', 'content': text},
      ],
      'temperature': 0.1,
      'max_tokens': 2048,
      'stream': false,
    };

    developer.log(
      'Sende Übersetzungsanfrage an LM Studio: $sourceName -> $targetName (${text.length} Zeichen)',
      name: 'TranslationService',
    );

    final response = await _client
        .post(
          Uri.parse('${TranslationConfig.lmStudioBaseUrl}$_chatCompletionsEndpoint'),
          headers: {'Content-Type': 'application/json'},
          body: jsonEncode(requestBody),
        )
        .timeout(const Duration(seconds: 30));

    if (response.statusCode != 200) {
      throw Exception('LM Studio API Fehler: ${response.statusCode} - ${response.body}');
    }

    return _extractTranslation(response.body, 'LM Studio');
  }

  /// Übersetzung mit Moonshot AI Cloud API (Kimi, API-Key erforderlich)
  Future<String> _translateWithMoonshot(
    String text,
    String sourceCode,
    String targetCode,
  ) async {
    final targetName = supportedLanguages[targetCode] ?? targetCode;
    final sourceName = supportedLanguages[sourceCode] ?? sourceCode;

    final systemPrompt =
        'You are a professional translator. Translate the user input from $sourceName to $targetName. '
        'Return ONLY the translation, without any additional text, explanations, or quotes.';

    final requestBody = {
      'model': TranslationConfig.moonshotModel,
      'messages': [
        {'role': 'system', 'content': systemPrompt},
        {'role': 'user', 'content': text},
      ],
      'temperature': 0.1,
      'max_tokens': 2048,
      'stream': false,
    };

    developer.log(
      'Sende Übersetzungsanfrage an Moonshot AI: $sourceName -> $targetName (${text.length} Zeichen)',
      name: 'TranslationService',
    );

    final response = await _client
        .post(
          Uri.parse('${TranslationConfig.moonshotBaseUrl}$_chatCompletionsEndpoint'),
          headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ${TranslationConfig.moonshotApiKey}',
          },
          body: jsonEncode(requestBody),
        )
        .timeout(const Duration(seconds: 30));

    if (response.statusCode != 200) {
      throw Exception('Moonshot AI API Fehler: ${response.statusCode} - ${response.body}');
    }

    return _extractTranslation(response.body, 'Moonshot AI');
  }

  /// Extrahiert den Übersetzungstext aus der OpenAI-kompatiblen API-Antwort
  String _extractTranslation(String responseBody, String backendLabel) {
    final data = jsonDecode(responseBody);
    final choices = data['choices'] as List<dynamic>?;

    if (choices == null || choices.isEmpty) {
      throw Exception('Leere Antwort von $backendLabel');
    }

    final message = choices[0]['message'] as Map<String, dynamic>?;
    String translated = message?['content']?.toString().trim() ?? '';

    // Anführungszeichen entfernen falls vorhanden
    if ((translated.startsWith('"') && translated.endsWith('"')) ||
        (translated.startsWith("'") && translated.endsWith("'"))) {
      translated = translated.substring(1, translated.length - 1);
    }

    return translated;
  }

  /// Hilfsmethode: Erkennt die Sprache eines Textes
  Future<String> _detectLanguage(String text) async {
    // Für kurze Texte: Default zu Englisch
    if (text.length < 10) {
      return 'en';
    }

    // Einfache Heuristik basierend auf häufigen Wörtern/Zeichen
    final lowerText = text.toLowerCase();

    // Deutsche Wörter prüfen
    final germanWords = ['der', 'die', 'das', 'und', 'ist', 'zu', 'den', 'mit', 'von', 'für'];
    if (germanWords.any((word) =>
        lowerText.contains(' $word ') || lowerText.startsWith('$word '))) {
      return 'de';
    }

    // Französische Wörter prüfen
    final frenchWords = ['le', 'la', 'les', 'et', 'est', 'pour', 'dans', 'sur', 'avec', 'une'];
    if (frenchWords.any((word) =>
        lowerText.contains(' $word ') || lowerText.startsWith('$word '))) {
      return 'fr';
    }

    // Spanische Wörter prüfen
    final spanishWords = ['el', 'la', 'los', 'las', 'y', 'es', 'para', 'con', 'por', 'del'];
    if (spanishWords.any((word) =>
        lowerText.contains(' $word ') || lowerText.startsWith('$word '))) {
      return 'es';
    }

    // Polnische Wörter prüfen
    final polishWords = ['jest', 'i', 'w', 'na', 'do', 'nie', 'się', 'tego', 'tej'];
    if (polishWords.any((word) =>
        lowerText.contains(' $word ') || lowerText.startsWith('$word '))) {
      return 'pl';
    }

    // Russische/Cyrillische Zeichen prüfen
    if (RegExp(r'[а-яА-Я]').hasMatch(text)) {
      return 'ru';
    }

    // Türkische Zeichen prüfen
    if (RegExp(r'[çğıöşüÇĞİÖŞÜ]').hasMatch(text)) {
      return 'tr';
    }

    // Estnische Zeichen prüfen
    if (RegExp(r'[äöüõÄÖÜÕ]').hasMatch(text)) {
      return 'et';
    }

    // Lettische Zeichen prüfen
    if (RegExp(r'[āčēģīķļņšūžĀČĒĢĪĶĻŅŠŪŽ]').hasMatch(text)) {
      return 'lv';
    }

    // Litauische Zeichen prüfen
    if (RegExp(r'[ąčęėįšųūžĄČĘĖĮŠŲŪŽ]').hasMatch(text)) {
      return 'lt';
    }

    // Arabische Zeichen prüfen
    if (RegExp(r'[\u0600-\u06FF]').hasMatch(text)) {
      return 'ar';
    }

    // Chinesische/Japanische/Koreanische Zeichen prüfen
    if (RegExp(r'[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff]').hasMatch(text)) {
      return 'zh';
    }

    // Default: Englisch
    return 'en';
  }

  /// Prüft ob die Übersetzung übersprungen werden sollte
  bool _shouldSkipTranslation(String text) {
    // Numerische Werte nicht übersetzen
    if (RegExp(r'^\d+$').hasMatch(text.trim())) {
      return true;
    }

    // Sehr kurze Codes nicht übersetzen
    if (text.trim().length <= 2) {
      return true;
    }

    // E-Mail Adressen nicht übersetzen
    if (text.contains('@') && text.contains('.')) {
      return true;
    }

    // URLs nicht übersetzen
    if (text.startsWith('http://') || text.startsWith('https://')) {
      return true;
    }

    return false;
  }

  /// Prüft ob ein Übersetzungsmodell verfügbar ist
  Future<bool> isModelAvailable() async {
    return _checkAvailability();
  }

  /// Gibt detaillierte Verfügbarkeitsinformationen zurück
  Future<Map<String, dynamic>> getAvailabilityInfo() async {
    final isOnline = await _checkAvailability();
    return {
      'isAvailable': isOnline,
      'backend': _backendName,
      'targetLanguage': _targetLanguageCode,
      'endpoint': _activeEndpoint,
      'platform': Platform.operatingSystem,
    };
  }

  /// Gibt die aktuell eingestellte Zielsprache zurück
  String get targetLanguageCode => _targetLanguageCode;

  /// Gibt den Anzeigenamen der aktuellen Sprache zurück
  String get targetLanguageDisplayName {
    return supportedLanguages[_targetLanguageCode] ?? _targetLanguageCode;
  }

  /// Gibt eine Liste aller verfügbaren Sprachen zurück
  List<MapEntry<String, String>> getAvailableLanguages() {
    return supportedLanguages.entries.toList();
  }

  /// Gibt den Anzeigenamen einer Sprache zurück
  String getLanguageDisplayName(String code) {
    return supportedLanguages[code] ?? code;
  }

  /// Schließt den Service und gibt Ressourcen frei
  Future<void> dispose() async {
    _client.close();
    _isAvailable = false;

    developer.log('TranslationService disposed', name: 'TranslationService');
  }
}
