import 'dart:developer';

import 'package:data_channel/data_channel.dart';
import 'package:injectable/injectable.dart';
import 'package:sqflite/sqflite.dart';

import '../../../common/constant/app_constant.dart';
import '../../../common/database/database_helper.dart';
import '../../../domain/category/category.dart';
import '../category_dtos.dart';

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

  CategoryLocalDataProvider(this._databaseHelper);

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

  Future<DC<CategoryFailure, void>> saveCategoriesBatch(
    List<CategoryDto> categories, {
    bool clearFirst = false,
  }) async {
    final db = await _databaseHelper.database;

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

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

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

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

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

  Future<DC<CategoryFailure, List<CategoryDto>>> getCachedCategories({
    int page = 1,
    int limit = 10,
    bool isActive = true,
    String? search,
  }) async {
    final cacheKey = _generateCacheKey(page, limit, isActive, 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) {
          log(
            '🚀 Cache HIT: $cacheKey (${_queryCache[cacheKey]!.length} categories)',
            name: _logName,
          );
          return DC.data(_queryCache[cacheKey]!);
        }
      }

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

      // Cache miss, query database
      final result = await getCategories(
        page: page,
        limit: limit,
        isActive: isActive,
        search: search,
      );

      // Check if result has data or error
      if (result.hasData) {
        final categories = result.data!;

        // Store in cache
        _queryCache[cacheKey] = categories;
        _cacheTimestamps[cacheKey] = now;

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

        return DC.data(categories);
      } else {
        // Return error from database query
        return DC.error(result.error!);
      }
    } catch (e, s) {
      log(
        '❌ Error getting cached categories',
        name: _logName,
        error: e,
        stackTrace: s,
      );
      return DC.error(CategoryFailure.localStorageError(e.toString()));
    }
  }

  Future<DC<CategoryFailure, List<CategoryDto>>> getCategories({
    int page = 1,
    int limit = 10,
    bool isActive = true,
    String? search,
  }) async {
    final db = await _databaseHelper.database;

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

      // Note: Assuming is_active will be added to database schema
      if (isActive) {
        query += ' AND is_active = ?';
        whereArgs.add(1);
      }

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

      // query += ' ORDER BY name ASC';

      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 categories = maps.map((map) => CategoryDto.fromMap(map)).toList();

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

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

  Future<DC<CategoryFailure, CategoryDto>> getCategoryById(String id) async {
    final db = await _databaseHelper.database;

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

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

      final category = CategoryDto.fromMap(maps.first);
      log('✅ Category found: ${category.name}', name: _logName);
      return DC.data(category);
    } catch (e, s) {
      log(
        '❌ Error getting category by ID',
        name: _logName,
        error: e,
        stackTrace: s,
      );
      return DC.error(CategoryFailure.localStorageError(e.toString()));
    }
  }

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

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

      if (isActive) {
        query += ' AND is_active = ?';
        whereArgs.add(1);
      }

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

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

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

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

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

      final activeCount =
          Sqflite.firstIntValue(
            await db.rawQuery(
              'SELECT COUNT(*) FROM categories WHERE is_active = 1',
            ),
          ) ??
          0;

      final stats = {
        'total_categories': categoryCount,
        'active_categories': activeCount,
        'cache_entries': _queryCache.length,
        'last_updated': DateTime.now().toIso8601String(),
      };

      log('📊 Category Database Stats: $stats', name: _logName);
      return DC.data(stats);
    } catch (e, s) {
      log(
        '❌ Error getting category database stats',
        name: _logName,
        error: e,
        stackTrace: s,
      );
      return DC.error(
        CategoryFailure.localStorageError(
          'Gagal memuat statistik database: $e',
        ),
      );
    }
  }

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

    try {
      await db.delete('categories');
      clearCache();
      log('🗑️ All categories cleared from local DB');
    } catch (e) {
      log('❌ Error clearing categories: $e');
      rethrow;
    }
  }

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

  String _generateCacheKey(int page, int limit, bool isActive, String? search) {
    return 'categories_${page}_${limit}_${isActive}_${search ?? 'null'}';
  }

  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 category cache cleared: ${expiredKeys.length} entries',
        name: _logName,
      );
    }
  }
}
