import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:sqflite/sqflite.dart';

import '../data/api_response.dart';
import '../models/models.dart';
import '../services/services.dart';

class PedidoViewModel extends ChangeNotifier {
  String _busqueda = "";
  String get busqueda => _busqueda;

  List<Pedido> _pedidos = [];
  Pedido? _selectedPedido;
  bool _isLoading = false;

  int _currentPage = 1;
  int _totalPedidos = 0;
  int _limit = 10;

  int get currentPage => _currentPage;
  int get totalPedidos => _totalPedidos;
  int get totalPages => (_totalPedidos / _limit).ceil();

  List<Pedido> get pedidos => _pedidos;
  Pedido? get selectedPedido => _selectedPedido;
  bool get isLoading => _isLoading;

  void setIsLoading(bool loading) {
    _isLoading = loading;
    notifyListeners();
  }

  Future<bool> guardarPedidoLocal({required Pedido pedido}) async {
    RepoService<Pedido> repoPedido = RepoService<Pedido>();

    if (pedido.id == null || pedido.id == 0) {
      int nextFolio = await repoPedido.obtenerProximoFolio();
      pedido.folio = nextFolio;
    }

    try {
      // Verifica si el pedido ya existe en la base de datos
      Pedido? pedidoExistente = await repoPedido.obtenerPorId(pedido.id!);

      if (pedidoExistente != null) {
        // Si existe, actualiza
        await repoPedido.guardar(pedido);
      } else {
        // Si no existe, inserta como nuevo
        int idPedido = await repoPedido.guardarLocal(pedido);
        pedido.id = idPedido;
      }

      RepoService<PedidoProducto> repoPedidoProducto =
          RepoService<PedidoProducto>();
      RepoService<PedidoProductoTopping> repoPedidoProductoTopping =
          RepoService<PedidoProductoTopping>();

      // Manejo de productos asociados al pedido
      for (var producto in pedido.productos) {
        PedidoProducto pedidoProducto = PedidoProducto(
          idPedido: pedido.id,
          idProducto: producto.idProducto,
          cantidad: producto.cantidad,
          costoUnitario: producto.costoUnitario,
          comentario: producto.comentario,
        );

        if (producto.id != null && producto.id! > 0) {
          await repoPedidoProducto.guardarLocal(pedidoProducto);
        } else {
          int idPedidoProducto =
              await repoPedidoProducto.guardarLocal(pedidoProducto);

          for (var topping in producto.toppings) {
            PedidoProductoTopping pedidoProductoTopping = PedidoProductoTopping(
              idPedidoProducto: idPedidoProducto,
              idTopping: topping.idTopping,
              idCategoria: topping.idCategoria,
            );
            await repoPedidoProductoTopping.guardarLocal(pedidoProductoTopping);
          }
        }
      }

      notifyListeners();
      return true;
    } catch (e) {
      print('Error al guardar el pedido: $e');
      return false;
    }
  }

  Future<bool> guardarPedidoConProductos({
    required Pedido pedido,
    required List<ItemCarrito> carrito,
  }) async {
    try {
      RepoService<Pedido> repoPedido = RepoService<Pedido>();
      RepoService<PedidoProducto> repoPedidoProducto =
          RepoService<PedidoProducto>();
      RepoService<PedidoProductoTopping> repoPedidoProductoTopping =
          RepoService<PedidoProductoTopping>();

      // Guardar el pedido en la base de datos
      if (pedido.id == null || pedido.id == 0) {
        int nextFolio = await repoPedido.obtenerProximoFolio();
        pedido.folio = nextFolio;
        pedido.id = await repoPedido.guardarLocal(pedido);
      } else {
        await repoPedido.guardar(pedido);
      }

      // Manejar los productos del carrito
      for (var item in carrito) {
        PedidoProducto pedidoProducto = PedidoProducto(
          idPedido: pedido.id,
          idProducto: item.producto.id,
          cantidad: item.cantidad,
          costoUnitario: item.producto.precio.toString(),
          sincronizado: null,
        );

        // Guardar o actualizar el producto
        int idPedidoProducto =
            await repoPedidoProducto.guardarPedidoProducto(pedidoProducto);

        // Manejar los toppings seleccionados
        for (var entry in item.selectedToppings.entries) {
          int categoriaId = entry.key;
          for (int toppingId in entry.value) {
            PedidoProductoTopping topping = PedidoProductoTopping(
              idPedidoProducto: idPedidoProducto,
              idTopping: toppingId,
              idCategoria: categoriaId,
            );
            await repoPedidoProductoTopping.guardarLocal(topping);
          }
        }
      }

      notifyListeners();
      return true;
    } catch (e) {
      print("Error al guardar el pedido con productos: $e");
      return false;
    }
  }

  Future<void> fetchLocalPedidos({int page = 1}) async {
    _isLoading = true;
    _currentPage = page;
    notifyListeners();

    RepoService<Pedido> repoPedido = RepoService<Pedido>();
    _totalPedidos = await repoPedido.contarPedidos();
    int offset = (_limit * (page - 1));
    List<Pedido> paginatedPedidos =
        await repoPedido.obtenerPedidosPaginados(_limit, offset);

    _pedidos = paginatedPedidos;
    _isLoading = false;
    notifyListeners();
  }

  void nextPage() {
    if (_currentPage < totalPages) {
      fetchLocalPedidosForScreen(page: _currentPage + 1);
    }
  }

  void previousPage() {
    if (_currentPage > 1) {
      fetchLocalPedidosForScreen(page: _currentPage - 1);
    }
  }

  Future<void> eliminarProductoDelPedido(int idProducto, int idPedido) async {
    RepoService<PedidoProducto> repoPedidoProducto =
        RepoService<PedidoProducto>();

    PedidoProducto? producto =
        await repoPedidoProducto.obtenerPorIdPedidoYProducto(
      idPedido: idPedido,
      idProducto: idProducto,
    );

    if (producto != null) {
      producto.eliminado = DateTime.now().toUtc();
      await repoPedidoProducto.guardar(producto);
      notifyListeners();
    }
  }

  Future<void> fetchLocalPedidosForScreen({int page = 1}) async {
    setIsLoading(true);
    RepoService<Pedido> repoPedido = RepoService<Pedido>();
    _currentPage = page;
    var db = await RepoService().db;

    int? count = Sqflite.firstIntValue(
        await db!.rawQuery('SELECT COUNT(*) FROM Pedido'));
    _totalPedidos = count ?? 0;

    int offset = (_limit * (page - 1));

    List<Pedido> localPedidos =
        await repoPedido.obtenerPedidosPaginados(_limit, offset);
    _pedidos = localPedidos;

    setIsLoading(false);
    notifyListeners();
  }

  Future<List<Pedido>> fetchAllLocalPedidos() async {
    setIsLoading(true);
    RepoService<Pedido> repoPedido = RepoService<Pedido>();
    List<Pedido> allPedidos = await repoPedido.obtenerTodos();
    setIsLoading(false);
    return allPedidos;
  }

  Future<List<Pedido>> fetchPedidosPorFechaSinLimit(
      DateTime startDate, DateTime endDate) async {
    setIsLoading(true);
    RepoService<Pedido> repoPedido = RepoService<Pedido>();
    List<Pedido> pedidos = await repoPedido.buscarPorFecha(startDate, endDate);
    setIsLoading(false);
    return pedidos;
  }

  Future<Pedido?> fetchPedidoConProductos(int idPedido) async {
    RepoService<Pedido> repoPedido = RepoService<Pedido>();
    Pedido? pedido = await repoPedido.obtenerPorId(idPedido);

    if (pedido != null) {
      RepoService<PedidoProducto> repoProducto = RepoService<PedidoProducto>();
      RepoService<Producto> repoProductoInfo = RepoService<Producto>();
      RepoService<Propinas> repoPropina = RepoService<Propinas>();
      List<PedidoProducto> productos =
          await repoProducto.obtenerPorIdPedido(idPedido);

      productos = productos.where((p) => p.eliminado == null).toList();

      for (var producto in productos) {
        Producto? prodInfo =
            await repoProductoInfo.obtenerProductoPorId(producto.idProducto!);
        if (prodInfo != null) {
          producto.producto = prodInfo;
        }

        RepoService<PedidoProductoTopping> repoTopping =
            RepoService<PedidoProductoTopping>();
        List<PedidoProductoTopping> toppings =
            await repoTopping.obtenerToppingsPorPedidoProducto(producto.id!);

        for (var topping in toppings) {
          Producto? toppingInfo =
              await repoProductoInfo.obtenerProductoPorId(topping.idTopping!);
          if (toppingInfo != null) {
            topping.topping = toppingInfo;
          }
        }

        producto.toppings = toppings;
        List<Propinas> propinas = await repoPropina.obtenerTodos<Propinas>(
            where: 'idPedido = ?', whereArgs: [idPedido]);
        pedido.propinas = propinas;
      }

      pedido.productos = productos;
    }

    return pedido;
  }

  Future<void> buscarPedidosPorFolio(String folio) async {
    setIsLoading(true);
    RepoService<Pedido> repoPedido = RepoService<Pedido>();
    List<Pedido> localPedidos = await repoPedido.buscarPorFolio(folio);
    _pedidos = localPedidos;

    setIsLoading(false);
    notifyListeners();
  }

  Future<void> buscarPedidosPorFecha(
      DateTime startDate, DateTime endDate) async {
    setIsLoading(true);
    RepoService<Pedido> repoPedido = RepoService<Pedido>();

    List<Pedido> localPedidos =
        await repoPedido.buscarPorFecha(startDate, endDate);
    _pedidos = localPedidos;

    setIsLoading(false);
    notifyListeners();
  }

  Future<List<Pedido>> buscarPorFecha(
      DateTime startDate, DateTime endDate) async {
    setIsLoading(true);
    RepoService<Pedido> repoPedido = RepoService<Pedido>();

    print('Consulta SQL de pedidos desde: $startDate hasta: $endDate');

    List<Pedido> pedidos = await repoPedido.buscarPorFecha(startDate, endDate);

    print('Pedidos obtenidos desde la base de datos: ${pedidos.length}');
    pedidos.forEach((pedido) => print(
        'Pedido Folio: ${pedido.folio}, Estatus: ${pedido.estatus}, Total: ${pedido.totalPedido}'));

    setIsLoading(false);
    notifyListeners();
    return pedidos;
  }

  Future<void> cancelarPedido(int idPedido) async {
    var db = await RepoService().db;
    await db?.update(
      'Pedido',
      {
        'estatus': 'CANCELADO',
        'sincronizado': null,
      },
      where: 'id = ?',
      whereArgs: [idPedido],
    );
    fetchLocalPedidosForScreen();
  }

  Future<bool> sincronizarPedidos() async {
    List<Pedido> pedidosNoSincronizados =
        await fetchAllLocalPedidosOrdenadosPorFecha();

    if (pedidosNoSincronizados.isNotEmpty) {
      Pedido pedidoNoSincronizado = pedidosNoSincronizados.first;

      if (pedidoNoSincronizado.productos.isEmpty) {
        pedidoNoSincronizado =
            await fetchPedidoConProductos(pedidoNoSincronizado.id) ??
                pedidoNoSincronizado;
      }

      Map<String, dynamic> pedidoJson =
          await prepararPedidoParaApi(pedidoNoSincronizado);

      //print('JSON enviado: $pedidoJson');

      var response = ApiResponse(await BaseService()
          .post('/pos/pedido/sincronizar', body: pedidoJson));

      if (response.isOk && response.detalle != null) {
        int idWeb = response.detalle!['id'];
        String sincronizado = response.detalle!['sincronizado'];

        await actualizarPedidoSincronizado(
            pedidoNoSincronizado.id!, idWeb, sincronizado);

        return true;
      } else {
        print('Error en la sincronización del pedido: ${response.mensaje}');
        return true;
      }
    } else {
      print('No se encontraron pedidos no sincronizados.');
      return false;
    }
  }

  Future<void> actualizarPedidoSincronizado(
      int idPedido, int idWeb, String sincronizado) async {
    var db = await RepoService().db;

    await db!.update(
      'Pedido',
      {
        'idWeb': idWeb,
        'sincronizado': sincronizado,
      },
      where: 'id = ?',
      whereArgs: [idPedido],
    );
  }

  Future<List<Pedido>> fetchAllLocalPedidosOrdenadosPorFecha() async {
    setIsLoading(true);
    RepoService<Pedido> repoPedido = RepoService<Pedido>();

    List<Pedido> allPedidos =
        await repoPedido.obtenerPedidosOrdenadosPorFecha();

    setIsLoading(false);
    return allPedidos;
  }

  Future<List<Pedido>> fetchPedidosNuevosByCorteId(String idCorteCaja) async {
    var db = await RepoService().db;

    List<Map<String, dynamic>> maps = await db!.query(
      'Pedido',
      where: 'estatus = ? AND idCorteCaja = ? AND eliminado IS NULL',
      whereArgs: ['NUEVO', idCorteCaja],
    );

    if (maps.isNotEmpty) {
      return maps.map((map) => Pedido.fromJson(map)).toList();
    }
    return [];
  }

  Future<void> actualizarCorteCajaEnPedidos(
      List<int> idsPedidos, String nuevoIdCorte) async {
    var db = await RepoService().db;

    for (int idPedido in idsPedidos) {
      await db!.update(
        'Pedido',
        {'idCorteCaja': nuevoIdCorte, 'sincronizado': null},
        where: 'id = ?',
        whereArgs: [idPedido],
      );
    }
  }

  Future<Map<String, dynamic>> prepararPedidoParaApi(Pedido pedido) async {
    String? claveSucursal =
        await RepoService().obtenerClaveSucursalSeleccionada();

    Map<String, dynamic> apiMap = pedido.toApi();

    apiMap['claveSucursal'] = claveSucursal;

    if (pedido.idWeb != null && pedido.idWeb! > 0) {
      apiMap['idWeb'] = pedido.idWeb;
    }

    return apiMap;
  }
}
