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

import '../data/api_response.dart';
import '../services/base_service.dart';
import '../models/models.dart';
import '../services/services.dart';
import '../services/repo_service.dart';
import '../views/producto/producto_imagen.dart';

class ProductoViewModel<T> extends ChangeNotifier {
  String _busqueda = "";
  String get busqueda => _busqueda;

  List<Producto> _productos = [];
  List<CategoriaProducto> _toppingCategories = [];
  bool _isLoading = false;
  int? _selectedCategoriaId = 0;
  Producto? _selectedProducto;

  List<Producto> get productos => _productos;
  Producto? get selectedProducto => _selectedProducto;
  bool get isLoading => _isLoading;

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

  int get currentPage => _currentPage;
  int get totalProducts => _totalProducts;
  int get totalPages => (_totalProducts / _limit).ceil();

  setBusqueda(String value) {
    _busqueda = value;
    notifyListeners();
  }

  void selectProducto(Producto producto) {
    _selectedProducto = producto;
    notifyListeners();
  }

  Future<void> fetchLocalAll({int page = 1}) async {
    _currentPage = page;
    var db = await RepoService().db;
    int? count = Sqflite.firstIntValue(
        await db!.rawQuery('SELECT COUNT(*) FROM Producto'));
    _totalProducts = count ?? 0;

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

    var query = await db.query('Producto',
        where: 'eliminado IS NULL',
        orderBy: 'id asc',
        limit: _limit,
        offset: offset);
    _productos = query.map((element) => Producto.fromJson(element)).toList();

    notifyListeners();
  }

  Future<void> fetchLocalByID({required int idCategoria}) async {
    var db = await RepoService().db;
    var query = await db!.query('Producto',
        where: 'idCategoria = ?',
        whereArgs: [idCategoria],
        orderBy: 'idLocal asc');
    List<Producto> aux = [];
    for (var element in query) {
      Producto producto = Producto.fromJson(element);
      aux.add(producto);
    }
    _productos = aux;
    notifyListeners();
  }

  Future<void> fetchAllByCategory(int idCategoria) async {
    var db = await RepoService().db;
    var query = await db!.query('Producto',
        where: 'idCategoria = ? and eliminado IS NULL',
        whereArgs: [idCategoria],
        orderBy: 'idLocal asc');
    _productos = query.map((e) => Producto.fromJson(e)).toList();
    notifyListeners();
  }

  Future<void> fetchLocalByName({required String nombre}) async {
    var db = await RepoService().db;
    var query = await db!.query(
      'Producto',
      where: 'nombre LIKE ?',
      whereArgs: ['%$nombre%'],
      orderBy: 'idLocal asc',
    );
    _productos = query.map((e) => Producto.fromJson(e)).toList();
    notifyListeners();
  }

  Future<int> addProducto(Producto producto) async {
    setIsLoading(true);
    try {
      int id = await RepoService().guardar(producto);
      fetchLocalAll();
      return id;
    } catch (e) {
      print('Error al agregar producto: $e');
      return -1;
    } finally {
      setIsLoading(false);
    }
  }

  Future<void> updateProducto(Producto producto) async {
    setIsLoading(true);
    try {
      int result = await RepoService().guardar(producto);
      print("Resultado: $result");
      fetchLocalAll();
    } catch (e) {
      print('Error al actualizar: $e');
    }
    setIsLoading(false);
  }

  Future<void> deleteProducto(int id) async {
    await RepoService().eliminar<Producto>(id);
    fetchLocalAll();
  }

  Future<bool> updateProductImagePath(int productId, String imagePath) async {
    setIsLoading(true);
    try {
      Producto? producto = await RepoService().obtenerProductoPorId(productId);
      producto!.imagen = imagePath;
      await RepoService().guardar(producto);
      notifyListeners();
      return true;
    } catch (e) {
      print('Error al actualizar la imagen del producto: $e');
      return false;
    } finally {
      setIsLoading(false);
    }
  }

  Future<List<CategoriaProducto>> fetchToppingCategories() async {
    var db = await RepoService().db;
    var query = await db!.query('CategoriaProducto',
        where: 'esToping = ?', whereArgs: [1], orderBy: 'id asc');
    _toppingCategories =
        query.map((element) => CategoriaProducto.fromJson(element)).toList();
    return _toppingCategories;
  }

  Future<List<Producto>> fetchProductsByCategory(int categoryId) async {
    var db = await RepoService().db;
    var query = await db!.query('Producto',
        where: 'idCategoria = ?', whereArgs: [categoryId], orderBy: 'id asc');
    return query.map((e) => Producto.fromJson(e)).toList();
  }

  Future<List<int>> obtenerToppingsPorProducto(int idProducto) async {
    var dbClient = await RepoService().db;
    var result = await dbClient!.query(
      'ProductoTopping',
      where: 'idProducto = ?',
      whereArgs: [idProducto],
    );
    return result.map((map) => map['idTopping'] as int).toList();
  }

  Future<Producto?> obtenerProductoPorId(int idProducto) async {
    Database? dbClient = await RepoService().db;
    List<Map> maps = await dbClient!
        .query('Producto', where: 'id = ?', whereArgs: [idProducto]);
    if (maps.isNotEmpty) {
      return Producto.fromJson(Map<String, dynamic>.from(maps.first));
    }
    return null;
  }

  Future<bool> sincronizarCategorias() async {
    String? claveSucursal =
        await RepoService().obtenerClaveSucursalSeleccionada();

    try {
      Map<String, String> parametros = {
        "claveSucursal": claveSucursal!,
        "limite": "-1"
      };
      final response = ApiResponse(await BaseService()
          .get('/pos/categoria', queryParameters: parametros));

      //print(response.resultados);

      if (response.isOk && response.resultados != null) {
        List<CategoriaProducto> categoriasApi = response.resultados!
            .map((json) => CategoriaProducto.fromApi(json))
            .toList();

        if (categoriasApi.isNotEmpty) {
          await RepoService().sincronizarCategorias(categoriasApi);
          notifyListeners();
          return true;
        }
      }
      return false;
    } catch (e) {
      print('Error al sincronizar categorías: $e');
      return false;
    }
  }

  Future<bool> sincronizarProductos() async {
    String? claveSucursal =
        await RepoService().obtenerClaveSucursalSeleccionada();

    try {
      Map<String, String> parametros = {
        "limite": "-1",
        "claveSucursal": claveSucursal!,
        "expand": "media"
      };
      final response = ApiResponse(await BaseService()
          .get('/pos/producto', queryParameters: parametros));

      if (response.isOk && response.resultados != null) {
        List<Producto> productosApi =
            response.resultados!.map((json) => Producto.fromApi(json)).toList();

        if (productosApi.isNotEmpty) {
          print("Productos API obtenidos: ${productosApi.length}");

          // Aquí mantengo tu lógica de descarga de imágenes.
          for (var productoApi in productosApi) {
            if (productoApi.media != null && productoApi.media!.isNotEmpty) {
              for (var media in productoApi.media!) {
                // Descargar y guardar la imagen localmente
                String? localImagePath = await downloadAndStoreImage(
                    media.ruta!, productoApi.id!, media.nombre!);

                if (localImagePath != null) {
                  productoApi.imagen = localImagePath;
                }
              }
            }
          }

          // Delegamos la sincronización de los productos al RepoService
          await RepoService().sincronizarProductos(productosApi);
          notifyListeners();
          return true;
        } else {
          print("No se encontraron productos en la API.");
        }
      } else {
        print("Error en la respuesta de la API o resultados nulos.");
      }
      return false;
    } catch (e) {
      print('Error al sincronizar productos: $e');
      return false;
    }
  }

  Future<bool> sincronizarProductoTopping() async {
    String? claveSucursal =
        await RepoService().obtenerClaveSucursalSeleccionada();

    try {
      Map<String, String> parametros = {
        "limite": "-1",
        "claveSucursal": claveSucursal!,
      };

      final response = ApiResponse(await BaseService()
          .get('/pos/producto-topping', queryParameters: parametros));

      if (response.isOk && response.resultados != null) {
        List<ProductoTopping> toppingApi = response.resultados!
            .map((json) => ProductoTopping.fromApi(json))
            .toList();

        if (toppingApi.isNotEmpty) {
          print("Producto Toppings API obtenidos: ${toppingApi.length}");

          await RepoService().sincronizarProductoTopping(toppingApi);
          notifyListeners();
          return true;
        } else {
          print("No se encontraron toppings de producto en la API.");
        }
      } else {
        print("Error en la respuesta de la API o resultados nulos.");
      }
      return false;
    } catch (e) {
      print('Error al sincronizar producto toppings: $e');
      return false;
    }
  }

  Future<void> sincronizarProductosYCategorias() async {
    setIsLoading(true);
    try {
      bool categoriasSincronizadas = await sincronizarCategorias();

      if (categoriasSincronizadas) {
        bool productosSincronizados = await sincronizarProductos();
        if (productosSincronizados) {
          bool toppingsSincronizados = await sincronizarProductoTopping();
          if (toppingsSincronizados) {
            await fetchLocalAll();
          }
        }
      }
      notifyListeners();
    } catch (e, stackTrace) {
      throw Exception(
          "Error al sincronizar productos, categorías y toppings: $e\n$stackTrace");
    } finally {
      setIsLoading(false);
    }
  }

  Future<bool> sincronizarProductosLocales() async {
    List<Producto> productosNoSincronizados =
        await RepoService().obtenerProductosNoSincronizadosOrdenadosPorFecha();

    if (productosNoSincronizados.isNotEmpty) {
      Producto productoNoSincronizado = productosNoSincronizados.first;

      Map<String, dynamic> productoJson =
          await prepararProductoParaApi(productoNoSincronizado);

      print('JSON Producto enviado: $productoJson');

      // Llamada a la API
      var response = ApiResponse(await BaseService()
          .post('/pos/producto/sincronizar', body: productoJson));

      if (response.isOk && response.detalle != null) {
        int idWeb = response.detalle!['idWeb'];
        String sincronizado = response.detalle!['sincronizado'];
        await RepoService().actualizarProductoSincronizado(
            productoNoSincronizado.id!, idWeb, sincronizado);
        return true;
      } else {
        print('Error en la sincronización del producto: ${response.mensaje}');
        return true;
      }
    } else {
      print('No se encontraron productos no sincronizados.');
      return false;
    }
  }

  Future<Map<String, dynamic>> prepararProductoParaApi(
      Producto producto) async {
    String? claveSucursal =
        await RepoService().obtenerClaveSucursalSeleccionada();
    Map<String, dynamic> apiMap = producto.toJson();

    apiMap['claveSucursal'] = claveSucursal;

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

    return apiMap;
  }

  Future<Map<String, dynamic>> prepararCategoriaParaApi(
      CategoriaProducto categoria) async {
    String? claveSucursal =
        await RepoService().obtenerClaveSucursalSeleccionada();
    Map<String, dynamic> apiMap = categoria.toJson();
    // Asegúrate que CategoriaProducto tenga un toJson y si necesitas toApi, créalo similar a Pedido.

    apiMap['claveSucursal'] = claveSucursal;

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

    return apiMap;
  }

  Future<bool> sincronizarCategoriasLocales() async {
    // Obtener categorias no sincronizadas
    List<CategoriaProducto> categoriasNoSincronizadas =
        await RepoService().obtenerCategoriasNoSincronizadasOrdenadasPorFecha();

    if (categoriasNoSincronizadas.isNotEmpty) {
      CategoriaProducto categoriaNoSincronizada =
          categoriasNoSincronizadas.first;

      Map<String, dynamic> categoriaJson =
          await prepararCategoriaParaApi(categoriaNoSincronizada);

      print('JSON Categoria enviado: $categoriaJson');

      // Llamada a la API
      var response = ApiResponse(await BaseService()
          .post('/pos/categoria/sincronizar', body: categoriaJson));

      if (response.isOk && response.detalle != null) {
        int idWeb = response.detalle!['idWeb'];
        String sincronizado = response.detalle!['sincronizado'];
        await RepoService().actualizarCategoriaSincronizada(
            categoriaNoSincronizada.id!, idWeb, sincronizado);
        return true;
      } else {
        print(
            'Error en la sincronización de la categoría: ${response.mensaje}');
        return true;
      }
    } else {
      print('No se encontraron categorias no sincronizadas.');
      return false;
    }
  }

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

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

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