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';

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: 'idLocal 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 obtenerClaveSucursal();

    try {
      Map<String, String> parametros = {"claveSucursal": claveSucursal!};
      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 obtenerClaveSucursal();

    try {
      Map<String, String> parametros = {
        "limite": "-1",
        "claveSucursal": claveSucursal!
      };
      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) {
          await RepoService().sincronizarProductos(productosApi);
          notifyListeners();
          return true;
        }
      }
      return false;
    } catch (e) {
      print('Error al sincronizar productos: $e');
      return false;
    }
  }

  Future<void> sincronizarProductosYCategorias() async {
    print('Sincronizando productos');
    setIsLoading(true);
    try {
      bool categoriasSincronizadas = await sincronizarCategorias();
      print('Categorias sincronizadas: $categoriasSincronizadas');

      if (categoriasSincronizadas) {
        bool productosSincronizados = await sincronizarProductos();
        if (productosSincronizados) {
          await fetchLocalAll();
        }
        print('Productos sincronizados: $productosSincronizados');
      }
      notifyListeners();
    } catch (e, stackTrace) {
      // Capturar el mensaje detallado del error
      throw Exception(
          "Error al sincronizar productos y categorías: $e\n$stackTrace");
    } finally {
      setIsLoading(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);
    }
  }

  Future<String?> obtenerClaveSucursal() async {
    Variable? sucursal = await RepoService().obtenerPorNombre('Sucursal');
    return sucursal?.clave;
  }
}
