import 'dart:developer';
import 'dart:io';

import 'package:data_channel/data_channel.dart';
import 'package:injectable/injectable.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart' as p;

import '../../../common/constant/app_constant.dart';
import '../../../common/database/database_helper.dart';
import '../../../domain/product/product.dart';
import '../product_dtos.dart';

@injectable
class ProductLocalDataProvider {
  final DatabaseHelper _databaseHelper;
  final _logName = 'ProductLocalDataProvider';

  final Map<String, List<ProductDto>> _queryCache = {};
  final Duration _cacheExpiry = const Duration(
    minutes: AppConstant.cacheExpire,
  );
  final Map<String, DateTime> _cacheTimestamps = {};

  ProductLocalDataProvider(this._databaseHelper);

  Future<DC<ProductFailure, void>> saveProductsBatch(
    List<ProductDto> products, {
    bool clearFirst = false,
  }) async {
    final db = await _databaseHelper.database;

    try {
      await db.transaction((txn) async {
        if (clearFirst) {
          log('🗑️ Clearing existing products...', name: _logName);
          await txn.delete('product_variants');
          await txn.delete('products');
        }

        log('💾 Batch saving ${products.length} products...', name: _logName);

        // ✅ Batch insert products
        final productBatch = txn.batch();
        for (final product in products) {
          productBatch.insert(
            'products',
            product.toMap(),
            conflictAlgorithm: ConflictAlgorithm.replace,
          );
        }
        await productBatch.commit(noResult: true);

        // ✅ Batch insert variants
        final variantBatch = txn.batch();
        for (final product in products) {
          if (product.variants?.isNotEmpty == true) {
            // Delete existing variants
            variantBatch.delete(
              'product_variants',
              where: 'product_id = ?',
              whereArgs: [product.id],
            );

            // Insert variants
            for (final variant in product.variants!) {
              variantBatch.insert(
                'product_variants',
                variant.toMap(),
                conflictAlgorithm: ConflictAlgorithm.replace,
              );
            }
          }
        }
        await variantBatch.commit(noResult: true);
      });

      // ✅ Clear cache
      clearCache();
      log(
        '✅ Successfully batch saved ${products.length} products',
        name: _logName,
      );

      return DC.data(null);
    } catch (e, s) {
      log(
        '❌ Error batch saving products',
        name: _logName,
        error: e,
        stackTrace: s,
      );

      return DC.error(ProductFailure.dynamicErrorMessage(e.toString()));
    }
  }

  Future<DC<ProductFailure, List<ProductDto>>> getCachedProducts({
    int page = 1,
    int limit = 10,
    String? categoryId,
    String? search,
  }) async {
    final cacheKey = _generateCacheKey(page, limit, categoryId, search);
    final now = DateTime.now();

    try {
      // ✅ CHECK CACHE FIRST
      if (_queryCache.containsKey(cacheKey) &&
          _cacheTimestamps.containsKey(cacheKey)) {
        final cacheTime = _cacheTimestamps[cacheKey]!;
        if (now.difference(cacheTime) < _cacheExpiry) {
          final cachedProducts = _queryCache[cacheKey]!;
          log(
            '🚀 Cache HIT: $cacheKey (${cachedProducts.length} products)',
            name: _logName,
          );
          return DC.data(cachedProducts);
        }
      }

      log('📀 Cache MISS: $cacheKey, querying database...', name: _logName);

      // Cache miss → query database
      final result = await getProducts(
        page: page,
        limit: limit,
        categoryId: categoryId,
        search: search,
      );

      // ✅ Handle data/error dari getProducts()
      if (result.hasData) {
        final products = result.data!;

        // Simpan ke cache
        _queryCache[cacheKey] = products;
        _cacheTimestamps[cacheKey] = now;

        log(
          '💾 Cached ${products.length} products for key: $cacheKey',
          name: _logName,
        );

        return DC.data(products);
      } else {
        // Kalau error dari getProducts
        return DC.error(result.error!);
      }
    } catch (e, s) {
      log(
        '❌ Error getting cached products',
        name: _logName,
        error: e,
        stackTrace: s,
      );

      return DC.error(ProductFailure.localStorageError(e.toString()));
    }
  }

  Future<DC<ProductFailure, List<ProductDto>>> getProducts({
    int page = 1,
    int limit = 10,
    String? categoryId,
    String? search,
  }) async {
    final db = await _databaseHelper.database;

    try {
      String query = 'SELECT * FROM products WHERE 1=1';
      List<dynamic> whereArgs = [];

      if (categoryId != null && categoryId.isNotEmpty) {
        query += ' AND category_id = ?';
        whereArgs.add(categoryId);
      }

      if (search != null && search.isNotEmpty) {
        query += ' AND (name LIKE ? OR sku LIKE ? OR description LIKE ?)';
        whereArgs.add('%$search%');
        whereArgs.add('%$search%');
        whereArgs.add('%$search%');
      }

      query += ' ORDER BY created_at DESC';

      if (limit > 0) {
        query += ' LIMIT ?';
        whereArgs.add(limit);

        if (page > 1) {
          query += ' OFFSET ?';
          whereArgs.add((page - 1) * limit);
        }
      }

      final List<Map<String, dynamic>> maps = await db.rawQuery(
        query,
        whereArgs,
      );

      final List<ProductDto> products = [];
      for (final map in maps) {
        final variants = await _getProductVariants(db, map['id']);
        final product = ProductDto.fromMap(map, variants);
        products.add(product);
      }

      log(
        '📊 Retrieved ${products.length} products from database',
        name: _logName,
      );

      return DC.data(products);
    } catch (e, s) {
      log('❌ Error getting products', name: _logName, error: e, stackTrace: s);
      return DC.error(ProductFailure.localStorageError(e.toString()));
    }
  }

  Future<DC<ProductFailure, List<ProductDto>>> searchProductsOptimized(
    String query,
  ) async {
    final db = await _databaseHelper.database;

    try {
      log('🔍 Optimized search for: "$query"', name: _logName);

      // ✅ Smart query with prioritization
      final List<Map<String, dynamic>> maps = await db.rawQuery(
        '''
      SELECT * FROM products 
      WHERE name LIKE ? OR sku LIKE ? OR description LIKE ?
      ORDER BY 
        CASE 
          WHEN name LIKE ? THEN 1      -- Highest priority: name match
          WHEN sku LIKE ? THEN 2       -- Second priority: SKU match  
          ELSE 3                       -- Lowest priority: description
        END,
        name ASC
      LIMIT 50
    ''',
        [
          '%$query%', '%$query%', '%$query%',
          '$query%', '$query%', // Prioritize results that start with query
        ],
      );

      final List<ProductDto> products = [];
      for (final map in maps) {
        final variants = await _getProductVariants(db, map['id']);
        final product = ProductDto.fromMap(map, variants);
        products.add(product);
      }

      log(
        '🎯 Optimized search found ${products.length} results',
        name: _logName,
      );

      return DC.data(products);
    } catch (e, s) {
      log(
        '❌ Error in optimized search',
        name: _logName,
        error: e,
        stackTrace: s,
      );
      return DC.error(ProductFailure.localStorageError(e.toString()));
    }
  }

  Future<DC<ProductFailure, ProductDto>> getProductById(String id) async {
    final db = await _databaseHelper.database;

    try {
      final List<Map<String, dynamic>> maps = await db.query(
        'products',
        where: 'id = ?',
        whereArgs: [id],
      );

      if (maps.isEmpty) {
        log('❌ Product not found: $id', name: _logName);
        return DC.error(const ProductFailure.empty());
      }

      final variants = await _getProductVariants(db, id);
      final product = ProductDto.fromMap(maps.first, variants);

      log('✅ Product found: ${product.name}', name: _logName);
      return DC.data(product);
    } catch (e, s) {
      log(
        '❌ Error getting product by ID',
        name: _logName,
        error: e,
        stackTrace: s,
      );
      return DC.error(ProductFailure.localStorageError(e.toString()));
    }
  }

  Future<DC<ProductFailure, Map<String, dynamic>>> getDatabaseStats() async {
    final db = await _databaseHelper.database;

    try {
      final productCount =
          Sqflite.firstIntValue(
            await db.rawQuery('SELECT COUNT(*) FROM products'),
          ) ??
          0;

      final variantCount =
          Sqflite.firstIntValue(
            await db.rawQuery('SELECT COUNT(*) FROM product_variants'),
          ) ??
          0;

      final categoryCount =
          Sqflite.firstIntValue(
            await db.rawQuery(
              'SELECT COUNT(DISTINCT category_id) FROM products WHERE category_id IS NOT NULL',
            ),
          ) ??
          0;

      final dbSize = await _getDatabaseSize();

      final stats = {
        'total_products': productCount,
        'total_variants': variantCount,
        'total_categories': categoryCount,
        'database_size_mb': dbSize,
        'cache_entries': _queryCache.length,
        'cache_size_mb': _getCacheSize(),
      };

      log('📊 Database Stats: $stats', name: _logName);

      return DC.data(stats);
    } catch (e, s) {
      log(
        '❌ Error getting database stats',
        name: _logName,
        error: e,
        stackTrace: s,
      );
      return DC.error(ProductFailure.localStorageError(e.toString()));
    }
  }

  Future<int> getTotalCount({String? categoryId, String? search}) async {
    final db = await _databaseHelper.database;

    try {
      String query = 'SELECT COUNT(*) FROM products WHERE 1=1';
      List<dynamic> whereArgs = [];

      if (categoryId != null && categoryId.isNotEmpty) {
        query += ' AND category_id = ?';
        whereArgs.add(categoryId);
      }

      if (search != null && search.isNotEmpty) {
        query += ' AND (name LIKE ? OR sku LIKE ? OR description LIKE ?)';
        whereArgs.add('%$search%');
        whereArgs.add('%$search%');
        whereArgs.add('%$search%');
      }

      final result = await db.rawQuery(query, whereArgs);
      final count = Sqflite.firstIntValue(result) ?? 0;
      log(
        '📊 Total count: $count (categoryId: $categoryId, search: $search)',
        name: _logName,
      );
      return count;
    } catch (e) {
      log('❌ Error getting total count: $e', name: _logName);
      return 0;
    }
  }

  Future<bool> hasProducts() async {
    final count = await getTotalCount();
    final hasData = count > 0;
    log('🔍 Has products: $hasData ($count products)', name: _logName);
    return hasData;
  }

  Future<void> clearAllProducts() async {
    final db = await _databaseHelper.database;

    try {
      await db.transaction((txn) async {
        await txn.delete('product_variants');
        await txn.delete('products');
      });
      clearCache();
      log('🗑️ All products cleared from local DB', name: _logName);
    } catch (e) {
      log('❌ Error clearing products: $e', name: _logName);
      rethrow;
    }
  }

  Future<double> _getDatabaseSize() async {
    try {
      final dbPath = p.join(await getDatabasesPath(), AppConstant.dbName);
      final file = File(dbPath);
      if (await file.exists()) {
        final size = await file.length();
        return size / (1024 * 1024); // Convert to MB
      }
    } catch (e) {
      log('Error getting database size: $e', name: _logName);
    }
    return 0.0;
  }

  Future<List<ProductVariantDto>> _getProductVariants(
    Database db,
    String productId,
  ) async {
    try {
      final List<Map<String, dynamic>> maps = await db.query(
        'product_variants',
        where: 'product_id = ?',
        whereArgs: [productId],
        orderBy: 'name ASC',
      );

      return maps.map((map) => ProductVariantDto.fromMap(map)).toList();
    } catch (e) {
      log(
        '❌ Error getting variants for product $productId: $e',
        name: _logName,
      );
      return [];
    }
  }

  String _generateCacheKey(
    int page,
    int limit,
    String? categoryId,
    String? search,
  ) {
    return 'products_${page}_${limit}_${categoryId ?? 'null'}_${search ?? 'null'}';
  }

  double _getCacheSize() {
    double totalSize = 0;
    _queryCache.forEach((key, products) {
      totalSize += products.length * 0.001; // Rough estimate in MB
    });
    return totalSize;
  }

  void clearCache() {
    final count = _queryCache.length;
    _queryCache.clear();
    _cacheTimestamps.clear();
    log('🧹 Cache cleared: $count entries removed', name: _logName);
  }

  void clearExpiredCache() {
    final now = DateTime.now();
    final expiredKeys = <String>[];

    _cacheTimestamps.forEach((key, timestamp) {
      if (now.difference(timestamp) > _cacheExpiry) {
        expiredKeys.add(key);
      }
    });

    for (final key in expiredKeys) {
      _queryCache.remove(key);
      _cacheTimestamps.remove(key);
    }

    if (expiredKeys.isNotEmpty) {
      log('⏰ Expired cache cleared: ${expiredKeys.length} entries');
    }
  }
}
