import 'dart:async';
import 'dart:developer';

import 'package:bloc/bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:injectable/injectable.dart';

import '../../domain/category/category.dart';
import '../../domain/product/product.dart';

part 'sync_event.dart';
part 'sync_state.dart';
part 'sync_bloc.freezed.dart';

enum SyncStep { categories, products, variants, completed }

class SyncStats {
  final int totalProducts;
  final int totalCategories;
  final int totalVariants;
  final double databaseSizeMB;

  SyncStats({
    required this.totalProducts,
    required this.totalCategories,
    required this.totalVariants,
    required this.databaseSizeMB,
  });
}

@injectable
class SyncBloc extends Bloc<SyncEvent, SyncState> {
  final IProductRepository _productRepository;
  final ICategoryRepository _categoryRepository;

  Timer? _progressTimer;
  bool _isCancelled = false;

  SyncBloc(this._productRepository, this._categoryRepository)
    : super(SyncState.initial()) {
    on<SyncEvent>(_onSyncEvent);
  }

  Future<void> _onSyncEvent(SyncEvent event, Emitter<SyncState> emit) {
    return event.map(
      startSync: (e) async {
        log('🔄 Starting full data sync (categories + products)...');
        _isCancelled = false;

        emit(
          state.copyWith(
            isSyncing: true,
            currentStep: SyncStep.categories,
            progress: 0.05,
            message: 'Membersihkan data lama...',
            errorMessage: null,
          ),
        );

        try {
          // Step 1: Clear existing local data
          await _productRepository.clearAllProducts();
          await _categoryRepository.clearAllCategories();

          if (_isCancelled) return;

          // Step 2: Sync categories first
          await _syncCategories(emit);

          if (_isCancelled) return;

          // Step 3: Sync products
          await _syncProducts(emit);

          if (_isCancelled) return;

          // Step 4: Final stats
          emit(
            state.copyWith(
              currentStep: SyncStep.completed,
              progress: 0.95,
              message: 'Menyelesaikan sinkronisasi...',
            ),
          );

          final stats = await _generateSyncStats();

          emit(
            state.copyWith(
              isSyncing: false,
              stats: stats,
              progress: 1.0,
              message: 'Sinkronisasi selesai ✅',
            ),
          );

          log('✅ Full sync completed successfully');
        } catch (e) {
          log('❌ Sync failed: $e');
          emit(
            state.copyWith(
              isSyncing: false,
              errorMessage: 'Gagal sinkronisasi: $e',
              message: 'Sinkronisasi gagal ❌',
            ),
          );
        }
      },
      cancelSync: (e) async {
        log('⏹️ Cancelling sync...');
        _isCancelled = true;
        _progressTimer?.cancel();
        emit(SyncState.initial());
      },
    );
  }

  Future<void> _syncCategories(Emitter<SyncState> emit) async {
    log('📁 Syncing categories...');

    emit(
      state.copyWith(
        isSyncing: true,
        currentStep: SyncStep.categories,
        progress: 0.1,
        message: 'Mengunduh kategori...',
        errorMessage: null,
      ),
    );

    try {
      // Gunakan CategoryRepository sync method
      final result = await _categoryRepository.syncAllCategories();

      await result.fold(
        (failure) async {
          throw Exception('Gagal sync kategori: $failure');
        },
        (successMessage) async {
          log('✅ Categories sync completed: $successMessage');

          emit(
            state.copyWith(
              currentStep: SyncStep.categories,
              progress: 0.2,
              message: 'Kategori berhasil diunduh ✅',
            ),
          );
        },
      );
    } catch (e) {
      log('❌ Category sync failed: $e');
      emit(
        state.copyWith(
          isSyncing: false,
          errorMessage: 'Gagal sync kategori: $e',
          message: 'Sinkronisasi kategori gagal ❌',
        ),
      );
      rethrow; // penting agar _onStartSync tahu kalau gagal
    }
  }

  Future<void> _syncProducts(Emitter<SyncState> emit) async {
    log('📦 Syncing products...');

    int page = 1;
    int totalSynced = 0;
    int? totalCount;
    int? totalPages;
    bool shouldContinue = true;

    while (!_isCancelled && shouldContinue) {
      // Hitung progress dinamis (kategori 0.0–0.2, produk 0.2–0.9)
      double progress = 0.2;
      if (totalCount != null && totalCount! > 0) {
        progress = 0.2 + (totalSynced / totalCount!) * 0.7;
      }

      emit(
        state.copyWith(
          isSyncing: true,
          currentStep: SyncStep.products,
          progress: progress,
          message: totalCount != null
              ? 'Mengunduh produk... ($totalSynced dari $totalCount)'
              : 'Mengunduh produk... ($totalSynced produk)',
          errorMessage: null,
        ),
      );

      final result = await _productRepository.getRemoteProducts(
        page: page,
        limit: 50, // ambil batch besar biar cepat
      );

      await result.fold(
        (failure) async {
          emit(
            state.copyWith(
              isSyncing: false,
              errorMessage: 'Gagal sync produk: $failure',
              message: 'Sinkronisasi produk gagal ❌',
            ),
          );
          throw Exception(failure);
        },
        (response) async {
          final products = response.products;
          final responseData = response;

          // Ambil total count & total page dari respons pertama
          if (page == 1) {
            totalCount = responseData.totalCount;
            totalPages = responseData.totalPages;
            log('📊 Total products to sync: $totalCount ($totalPages pages)');
          }

          if (products.isEmpty) {
            shouldContinue = false;
            return;
          }

          // Simpan batch produk ke local DB
          await _productRepository.saveProductsBatch(products);

          totalSynced += products.length;
          page++;

          log(
            '📦 Synced page ${page - 1}: ${products.length} products (Total: $totalSynced)',
          );

          // Cek apakah sudah selesai sync
          if (totalPages != null && page > totalPages!) {
            shouldContinue = false;
            return;
          }

          // Fallback jika pagination info tidak lengkap
          if (products.length < 50) {
            shouldContinue = false;
            return;
          }

          // Tambahkan delay kecil agar tidak overload server
          await Future.delayed(const Duration(milliseconds: 100));
        },
      );
    }

    // Selesai sync produk
    emit(
      state.copyWith(
        progress: 0.9,
        message: 'Sinkronisasi produk selesai ✅ ($totalSynced total)',
      ),
    );
  }

  Future<SyncStats> _generateSyncStats() async {
    try {
      log('📊 Generating sync statistics via repository...');

      // Jalankan kedua query secara paralel untuk efisiensi
      final results = await Future.wait([
        _productRepository.getDatabaseStats(),
        _categoryRepository.getDatabaseStats(),
      ]);

      // Default kosong
      Map<String, dynamic> productStats = {};
      Map<String, dynamic> categoryStats = {};

      // Ambil hasil product stats
      await results[0].fold(
        (failure) async {
          log('⚠️ Failed to get product stats: $failure');
        },
        (data) async {
          productStats = data;
        },
      );

      // Ambil hasil category stats
      await results[1].fold(
        (failure) async {
          log('⚠️ Failed to get category stats: $failure');
        },
        (data) async {
          categoryStats = data;
        },
      );

      // Bangun objek SyncStats akhir
      final stats = SyncStats(
        totalProducts: productStats['total_products'] ?? 0,
        totalCategories: categoryStats['total_categories'] ?? 0,
        totalVariants: productStats['total_variants'] ?? 0,
        databaseSizeMB:
            ((productStats['database_size_mb'] ?? 0.0) as num).toDouble() +
            ((categoryStats['database_size_mb'] ?? 0.0) as num).toDouble(),
      );

      log('✅ Sync stats generated: $stats');
      return stats;
    } catch (e, stack) {
      log('❌ Error generating sync stats: $e\n$stack');
      return SyncStats(
        totalProducts: 0,
        totalCategories: 0,
        totalVariants: 0,
        databaseSizeMB: 0.0,
      );
    }
  }

  @override
  Future<void> close() {
    _progressTimer?.cancel();
    return super.close();
  }
}
