import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:drift/drift.dart' as drift;
import 'package:search_engine/services/config_service.dart';
import 'package:search_engine/services/mimir_service.dart';

part 'database.g.dart';

class Draft {
  final String id;
  final String title;
  final DateTime date;
  final int activity;
  final String thumbnail;
  final int draft;
  final String locale;
  final String country;
  final String city;
  final String? body;
  final String? pdf;
  final String languagesCode;
  final Map<String, dynamic>? searchResultData;
  final int position;
  final int length;

  Draft({
    required this.id,
    required this.title,
    required this.date,
    required this.activity,
    required this.thumbnail,
    required this.draft,
    required this.locale,
    required this.country,
    required this.city,
    this.body,
    this.pdf,
    required this.languagesCode,
    this.searchResultData,
    this.position = 0,
    this.length = 0,
  });
}

class Messages extends Table {
  TextColumn get id => text()();

  TextColumn get country => text()();

  TextColumn get city => text()();

  DateTimeColumn get date => dateTime()();

  IntColumn get activity => integer()();

  IntColumn get draft => integer()();

  TextColumn get thumbnail => text()();

  @override
  Set<Column> get primaryKey => {id};
}

@TableIndex(name: 'title_index', columns: {#title})
@TableIndex(name: 'body_index', columns: {#body})
@TableIndex(name: 'message_id_index', columns: {#messageId})
class Translations extends Table {
  TextColumn get messageId => text()();

  TextColumn get title => text()();

  TextColumn get body => text()();

  TextColumn get languagesCode => text()();

  TextColumn get pdf => text().nullable()();

  @override
  Set<Column> get primaryKey => {messageId, languagesCode};
}

class Favorites extends Table {
  TextColumn get id => text()();

  @override
  Set<Column> get primaryKey => {id};
}

@DriftDatabase(tables: [Messages, Translations, Favorites])
class AppDatabase extends _$AppDatabase {
  AppDatabase() : super(_createConnection());

  static DatabaseConnection _createConnection() {
    return DatabaseConnection.delayed(Future(() async {
      final dbFolder = await getApplicationDocumentsDirectory();
      final filePath = p.join(dbFolder.path, 'LGCC_Search/internal/db.sqlite');

      final file = File(filePath);
      final database = NativeDatabase(file,
          logStatements: false, cachePreparedStatements: true);

      return DatabaseConnection(database);
    }));
  }

  @override
  int get schemaVersion => 1;

  Future<void> addMessages(List<Draft> drafts) async {
    if (drafts.isEmpty) {
      return; // No hay nada que insertar
    }

    try {
      // Preparar las listas para mensajes y traducciones
      List<MessagesCompanion> messagesList = [];
      List<TranslationsCompanion> translationsList = [];

      // Validar y preparar los datos
      for (final draft in drafts) {
        // Validar que el ID no esté vacío
        if (draft.id.isEmpty) {
          if (kDebugMode) {
            print('Saltando mensaje con ID vacío');
          }
          continue;
        }

        // Preparar el mensaje
        messagesList.add(MessagesCompanion(
          id: Value(draft.id),
          country: Value(draft.country),
          city: Value(draft.city),
          date: Value(draft.date),
          activity: Value(draft.activity),
          thumbnail: Value(draft.thumbnail),
          draft: Value(draft.draft),
        ));

        // Preparar la traducción
        translationsList.add(TranslationsCompanion(
          messageId: Value(draft.id),
          body: Value(draft.body ?? ''),
          languagesCode: Value(draft.languagesCode),
          title: Value(draft.title),
          pdf: Value(draft.pdf ?? ''),
        ));
      }

      // Ejecutar las inserciones en una transacción para garantizar la atomicidad
      await transaction(() async {
        // Insertar mensajes con manejo de conflictos
        for (var message in messagesList) {
          await into(messages).insert(
            message,
            onConflict: DoUpdate(
              (old) => MessagesCompanion(
                country: message.country,
                city: message.city,
                date: message.date,
                activity: message.activity,
                thumbnail: message.thumbnail,
                draft: message.draft,
              ),
              target: [messages.id],
            ),
          );
        }

        // Insertar traducciones con manejo de conflictos
        for (var translation in translationsList) {
          await into(translations).insert(
            translation,
            onConflict: DoUpdate(
              (old) => TranslationsCompanion(
                title: translation.title,
                body: translation.body,
                pdf: translation.pdf,
              ),
              target: [translations.messageId, translations.languagesCode],
            ),
          );
        }
      });
    } catch (e) {
      if (kDebugMode) {
        print('Error al insertar mensajes en la base de datos: $e');
      }
      // Reintento con enfoque más seguro en caso de error
      try {
        // Insertar uno por uno para identificar registros problemáticos
        for (int i = 0; i < drafts.length; i++) {
          final draft = drafts[i];
          try {
            // Insertar mensaje
            await into(messages).insert(
              MessagesCompanion(
                id: Value(draft.id),
                country: Value(draft.country),
                city: Value(draft.city),
                date: Value(draft.date),
                activity: Value(draft.activity),
                thumbnail: Value(draft.thumbnail),
                draft: Value(draft.draft),
              ),
              onConflict: DoUpdate(
                (old) => MessagesCompanion(
                  country: Value(draft.country),
                  city: Value(draft.city),
                  date: Value(draft.date),
                  activity: Value(draft.activity),
                  thumbnail: Value(draft.thumbnail),
                  draft: Value(draft.draft),
                ),
                target: [messages.id],
              ),
            );

            // Insertar traducción
            await into(translations).insert(
              TranslationsCompanion(
                messageId: Value(draft.id),
                body: Value(draft.body ?? ''),
                languagesCode: Value(draft.languagesCode),
                title: Value(draft.title),
                pdf: Value(draft.pdf ?? ''),
              ),
              onConflict: DoUpdate(
                (old) => TranslationsCompanion(
                  title: Value(draft.title),
                  body: Value(draft.body ?? ''),
                  pdf: Value(draft.pdf ?? ''),
                ),
                target: [translations.messageId, translations.languagesCode],
              ),
            );
          } catch (innerError) {
            if (kDebugMode) {
              print(
                  'Error al insertar mensaje #$i (ID: ${draft.id}): $innerError');
            }
            // Continuar con el siguiente registro
          }
        }
      } catch (fallbackError) {
        if (kDebugMode) {
          print('Error en el proceso de recuperación: $fallbackError');
        }
      }
    }
  }

  Future<List<Map<String, dynamic>>> getPdfList() async {
    final locale = await ConfigService.getLocale();
    try {
      final queryResult = await customSelect(
          "SELECT pdf, id, t.title, date, m.country, activity FROM messages LEFT JOIN translations  ON messages.id = translations.message_id WHERE pdf <> '' AND locale = '$locale'",
          readsFrom: {messages}).map((row) {
        return {
          'pdf': row.read<String>('pdf'),
          'title': row.read<String>('title')
        };
      }).get();
      return queryResult;
    } catch (e) {
      if (kDebugMode) {
        print('Error fetching PDF list: $e');
      }
      return [];
    }
  }

  Future<List<Draft>> getMessages(bool isDraft, [String? localeParam]) async {
    final locale = localeParam ?? await ConfigService.getLocale();
    if (locale.isEmpty) {
      return [];
    }

    try {
      // Consulta SQL optimizada sin usar INDEXED BY
      final queryResult = await customSelect(
        """
      SELECT DISTINCT
        m.id,
        m.date,
        m.activity,
        m.thumbnail,
        m.draft,
        m.country,
        m.city,
        t.languages_code as locale,
        t.title,
        t.pdf,
        t.languages_code
      FROM
        messages m
        JOIN translations t ON m.id = t.message_id
      WHERE
        m.draft = ?
        AND t.languages_code = ?
      ORDER BY m.date DESC
      LIMIT 20
      """,
        variables: [
          Variable<int>(isDraft ? 1 : 0),
          Variable.withString(locale),
        ],
        readsFrom: {messages, translations},
      ).map((row) {
        return Draft(
          id: row.read<String>('id'),
          title: row.read<String>('title'),
          date: row.read<DateTime>('date'),
          activity: row.read<int>('activity'),
          thumbnail: row.read<String>('thumbnail'),
          draft: row.read<int>('draft'),
          locale: row.read<String>('locale'),
          country: row.read<String>('country'),
          city: row.read<String>('city'),
          // Cargar el cuerpo solo cuando sea necesario para mejorar el rendimiento
          body: '',
          pdf: row.read<String?>('pdf') ?? '',
          languagesCode: row.read<String>('languages_code'),
        );
      }).get();

      return queryResult;
    } catch (e) {
      if (kDebugMode) {
        print('Error fetching messages: $e');
      }
      return [];
    }
  }

  // Nuevo método para cargar el cuerpo de un mensaje específico cuando sea necesario
  Future<String> getMessageBody(String messageId, String languageCode) async {
    try {
      final result = await customSelect(
        """
        SELECT body 
        FROM translations 
        WHERE message_id = ? AND languages_code = ?
        """,
        variables: [
          Variable.withString(messageId),
          Variable.withString(languageCode),
        ],
      ).getSingleOrNull();

      return result?.read<String?>('body') ?? '';
    } catch (e) {
      if (kDebugMode) {
        print('Error fetching message body: $e');
      }
      return '';
    }
  }

  Future<List<Draft>> getYearActivities(String year, String month) async {
    final locale = await ConfigService.getLocale();
    try {
      final queryResult = await customSelect(
        "SELECT DISTINCT m.id, t.title, m.date, m.activity, m.thumbnail, m.draft, t.languages_code as locale, m.country, m.city, t.body, t.pdf, t.languages_code, strftime('%Y', datetime(m.date, 'unixepoch')) AS year, strftime('%m', datetime(m.date, 'unixepoch')) AS month "
        "FROM messages m JOIN translations t ON m.id = t.message_id "
        "WHERE strftime('%Y', datetime(m.date, 'unixepoch')) = ? AND strftime('%m', datetime(m.date, 'unixepoch')) = ? AND t.languages_code = ? "
        "ORDER BY m.date DESC, m.activity DESC",
        variables: [
          Variable.withString(year),
          Variable.withString(month),
          Variable.withString(locale)
        ],
        readsFrom: {messages, translations},
      ).map((row) {
        final id = row.read<String>('id');
        final title = row.read<String>('title');
        final date = row.read<DateTime>('date');
        final activity = row.read<int>('activity');
        final thumbnail = row.read<String>('thumbnail');
        final draft = row.read<int>('draft');
        final locale = row.read<String>('locale');
        final country = row.read<String>('country');
        final city = row.read<String>('city');
        final body = row.read<String?>('body') ?? '';
        final pdf = row.read<String?>('pdf') ?? '';
        final languagesCode = row.read<String>('languages_code');
        return Draft(
          id: id,
          title: title,
          date: date,
          activity: activity,
          thumbnail: thumbnail,
          draft: draft,
          locale: locale,
          country: country,
          city: city,
          body: body,
          pdf: pdf,
          languagesCode: languagesCode,
        );
      }).get();

      return queryResult;
    } catch (e) {
      if (kDebugMode) {
        print('Error fetching activities: $e');
      }
      return [];
    }
  }

  Future<List<String>> getMonths(String year) async {
    try {
      final queryResult = await customSelect(
        "SELECT DISTINCT strftime('%m', datetime(date, 'unixepoch')) AS month FROM messages WHERE strftime('%Y', datetime(date, 'unixepoch')) = ? ORDER BY month ASC",
        variables: [Variable.withString(year)],
        readsFrom: {messages},
      ).map((row) {
        return row.read<String>('month');
      }).get();

      return queryResult;
    } catch (e) {
      if (kDebugMode) {
        print('Error fetching months: $e');
      }
      return [];
    }
  }

  Future<void> toggleFavorite(String id) async {
    final result = await customSelect(
      'SELECT COUNT(*) AS count FROM favorites WHERE id = ?',
      variables: [Variable.withString(id)],
    ).getSingle();

    final isFavorite = result.read<int>('count') > 0;

    if (isFavorite) {
      delete(favorites).delete(FavoritesCompanion(id: Value(id)));
    } else {
      into(favorites).insert(
        FavoritesCompanion(id: Value(id)),
      );
    }
  }

  Future<bool> checkFavorite(String id) async {
    final result = await customSelect(
      'SELECT DISTINCT COUNT(*) AS count FROM favorites WHERE id = ?',
      variables: [Variable.withString(id)],
    ).getSingle();
    final isFavorite = result.read<int>('count') > 0;
    return isFavorite;
  }

  Future<List<String>> getFavorites() async {
    final result = await customSelect('SELECT DISTINCT id FROM favorites')
        .map((row) => row.read<String>('id'))
        .get();
    return result;
  }

  // Método para obtener los años disponibles en la base de datos
  Future<List<String>> getAvailableYears() async {
    try {
      final queryResult = await customSelect(
        "SELECT DISTINCT strftime('%Y', datetime(date, 'unixepoch')) AS year FROM messages ORDER BY year DESC",
        readsFrom: {messages},
      ).map((row) {
        return row.read<String>('year');
      }).get();

      return queryResult;
    } catch (e) {
      if (kDebugMode) {
        print('Error fetching available years: $e');
      }
      return [];
    }
  }

  // Método para obtener mensajes filtrados por año y mes
  Future<List<Draft>> getFilteredMessages(String year, String month,
      [String? localeParam]) async {
    final locale = localeParam ?? await ConfigService.getLocale();
    if (locale.isEmpty) {
      return [];
    }

    try {
      final queryResult = await customSelect(
        """
        SELECT DISTINCT
          m.id,
          m.date,
          m.activity,
          m.thumbnail,
          m.draft,
          m.country,
          m.city,
          t.languages_code as locale,
          t.title,
          t.pdf,
          t.languages_code
        FROM
          messages m
          JOIN translations t ON m.id = t.message_id
        WHERE
          strftime('%Y', datetime(m.date, 'unixepoch')) = ?
          AND strftime('%m', datetime(m.date, 'unixepoch')) = ?
          AND t.languages_code = ?
        ORDER BY m.date DESC
        """,
        variables: [
          Variable.withString(year),
          Variable.withString(month),
          Variable.withString(locale),
        ],
        readsFrom: {messages, translations},
      ).map((row) {
        return Draft(
          id: row.read<String>('id'),
          title: row.read<String>('title'),
          date: row.read<DateTime>('date'),
          activity: row.read<int>('activity'),
          thumbnail: row.read<String>('thumbnail'),
          draft: row.read<int>('draft'),
          locale: row.read<String>('locale'),
          country: row.read<String>('country'),
          city: row.read<String>('city'),
          body: '',
          pdf: row.read<String?>('pdf') ?? '',
          languagesCode: row.read<String>('languages_code'),
        );
      }).get();

      return queryResult;
    } catch (e) {
      if (kDebugMode) {
        print('Error fetching filtered messages: $e');
      }
      return [];
    }
  }

  // Método para obtener todos los mensajes (borradores y no borradores)
  Future<List<Draft>> getAllMessages([String? localeParam, int? limit]) async {
    final locale = localeParam ?? await ConfigService.getLocale();
    if (locale.isEmpty) {
      return [];
    }

    try {
      // Consulta SQL optimizada para obtener todos los mensajes
      var query = """
      SELECT DISTINCT
        m.id,
        m.date,
        m.activity,
        m.thumbnail,
        m.draft,
        m.country,
        m.city,
        t.languages_code as locale,
        t.title,
        t.body,
        t.pdf,
        t.languages_code
      FROM
        messages m
        JOIN translations t ON m.id = t.message_id
      WHERE
        t.languages_code = ?
      ORDER BY m.date DESC
      """;

      // Añadir límite si se especifica
      if (limit != null) {
        query += " LIMIT $limit";
      }

      final queryResult = await customSelect(
        query,
        variables: [
          Variable.withString(locale),
        ],
        readsFrom: {messages, translations},
      ).map((row) {
        return Draft(
          id: row.read<String>('id'),
          title: row.read<String>('title'),
          date: row.read<DateTime>('date'),
          activity: row.read<int>('activity'),
          thumbnail: row.read<String>('thumbnail'),
          draft: row.read<int>('draft'),
          locale: row.read<String>('locale'),
          country: row.read<String>('country'),
          city: row.read<String>('city'),
          body: row.read<String?>('body') ?? '',
          pdf: row.read<String?>('pdf') ?? '',
          languagesCode: row.read<String>('languages_code'),
        );
      }).get();

      return queryResult;
    } catch (e) {
      if (kDebugMode) {
        print('Error fetching all messages: $e');
      }
      return [];
    }
  }

  Future<List<Draft>> searchMessages(String query, String languagesCode) async {
    try {
      final mimirService = MimirService();
      await mimirService.initialize();

      // Buscar en Mimir para obtener los IDs relevantes y metadatos
      final searchResult = await mimirService.search(query, languagesCode);

      // Extraer IDs de los resultados
      List<Map<String, dynamic>> searchResultsData = searchResult['results'];
      List<String> docIds =
          searchResultsData.map((doc) => doc['id'] as String).toList();

      if (docIds.isEmpty) {
        return [];
      }

      // Convertir los IDs en una cadena para la consulta SQL
      final String idList = docIds.map((id) => "'$id'").join(',');

      // Buscar los documentos completos en la base de datos
      final queryResult = await customSelect(
        '''
        SELECT DISTINCT
          m.id,
          m.date,
          m.activity,
          m.thumbnail,
          m.draft,
          m.country,
          m.city,
          t.languages_code as locale,
          t.title,
          t.pdf,
          t.languages_code,
          t.body
        FROM
          messages m
          JOIN translations t ON m.id = t.message_id
        WHERE
          m.id IN ($idList) AND t.languages_code = ?
        ORDER BY m.date DESC
        ''',
        variables: [Variable.withString(languagesCode)],
        readsFrom: {messages, translations},
      ).map((row) {
        final String id = row.read<String>('id');

        // Buscar los datos del resultado de búsqueda correspondiente
        Map<String, dynamic>? resultData;
        for (var data in searchResultsData) {
          if (data['id'] == id) {
            resultData = data;
            break;
          }
        }

        return Draft(
          id: id,
          title: row.read<String>('title'),
          date: row.read<DateTime>('date'),
          activity: row.read<int>('activity'),
          thumbnail: row.read<String>('thumbnail'),
          draft: row.read<int>('draft'),
          locale: row.read<String>('locale'),
          country: row.read<String>('country'),
          city: row.read<String>('city'),
          body: row.read<String?>('body'),
          pdf: row.read<String?>('pdf') ?? '',
          languagesCode: row.read<String>('languages_code'),
          searchResultData: {
            ...resultData ?? {},
            'allResultIds': docIds,
            'mimirSearchResult': searchResult,
          },
        );
      }).get();

      return queryResult;
    } catch (e) {
      if (kDebugMode) {
        print('Error searching messages: $e');
      }
      return [];
    }
  }

  // Método para obtener todos los PDFs disponibles
  Future<List<Draft>> getAvailablePdfs(String locale) async {
    try {
      final query = """
      SELECT DISTINCT
        m.id,
        m.date,
        m.activity,
        m.thumbnail,
        m.draft,
        m.country,
        m.city,
        t.languages_code as locale,
        t.title,
        t.pdf,
        t.languages_code
      FROM
        messages m
        JOIN translations t ON m.id = t.message_id
      WHERE
        t.languages_code = ?
        AND t.pdf IS NOT NULL
        AND t.pdf != ''
      ORDER BY m.date DESC
      """;

      final queryResult = await customSelect(
        query,
        variables: [
          Variable.withString(locale),
        ],
        readsFrom: {messages, translations},
      ).map((row) {
        return Draft(
          id: row.read<String>('id'),
          title: row.read<String>('title'),
          date: row.read<DateTime>('date'),
          activity: row.read<int>('activity'),
          thumbnail: row.read<String>('thumbnail'),
          draft: row.read<int>('draft'),
          locale: row.read<String>('locale'),
          country: row.read<String>('country'),
          city: row.read<String>('city'),
          body: '',
          pdf: row.read<String?>('pdf') ?? '',
          languagesCode: row.read<String>('languages_code'),
        );
      }).get();

      return queryResult;
    } catch (e) {
      if (kDebugMode) {
        print('Error fetching available PDFs: $e');
      }
      return [];
    }
  }

  // Método para obtener mensajes por IDs
  Future<List<Draft>> getMessagesByIds(List<String> ids, String locale) async {
    if (ids.isEmpty) return [];

    try {
      // Convertir la lista de IDs a un formato que pueda usarse en la consulta SQL
      final String idList = ids.map((id) => "'$id'").join(',');

      final queryResult = await customSelect(
        '''
        SELECT DISTINCT
          m.id,
          m.date,
          m.activity,
          m.thumbnail,
          m.draft,
          m.country,
          m.city,
          t.languages_code as locale,
          t.title,
          t.body,
          t.pdf,
          t.languages_code
        FROM
          messages m
          JOIN translations t ON m.id = t.message_id
        WHERE
          m.id IN ($idList) AND t.languages_code = ?
        ORDER BY m.date DESC
        ''',
        variables: [Variable.withString(locale)],
        readsFrom: {messages, translations},
      ).map((row) {
        return Draft(
          id: row.read<String>('id'),
          title: row.read<String>('title'),
          date: row.read<DateTime>('date'),
          activity: row.read<int>('activity'),
          thumbnail: row.read<String>('thumbnail'),
          draft: row.read<int>('draft'),
          locale: row.read<String>('locale'),
          country: row.read<String>('country'),
          city: row.read<String>('city'),
          body: row.read<String?>('body') ?? '',
          pdf: row.read<String?>('pdf') ?? '',
          languagesCode: row.read<String>('languages_code'),
        );
      }).get();

      if (kDebugMode) {
        print(
            'Encontrados ${queryResult.length} mensajes de ${ids.length} IDs');
      }

      return queryResult;
    } catch (e) {
      if (kDebugMode) {
        print('Error obteniendo mensajes por IDs: $e');
      }
      return [];
    }
  }
}
