import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:injectable/injectable.dart';

import '../../../../../../core/errors/failures.dart';
import '../../../../../../core/constants/api_constants.dart';
import '../../../domain/entities/product_entity.dart';
import '../../../domain/usecases/place_order_usecase.dart';
import '../../../domain/usecases/get_cart_usecase.dart';
import 'checkout_event.dart';
import 'checkout_state.dart';

@injectable
class CheckoutBloc extends Bloc<CheckoutEvent, CheckoutState> {
  final PlaceOrderUseCase _placeOrderUseCase;
  final GetCartUseCase _getCartUseCase;

  CheckoutBloc(
    this._placeOrderUseCase,
    this._getCartUseCase,
  ) : super(CheckoutInitial()) {
    on<CheckoutInitialized>(_onCheckoutInitialized);
    on<WalletsLoaded>(_onWalletsLoaded);
    on<WalletSelected>(_onWalletSelected);
    on<ShippingAddressUpdated>(_onShippingAddressUpdated);
    on<ShippingMethodSelected>(_onShippingMethodSelected);
    on<CouponApplied>(_onCouponApplied);
    on<CouponRemoved>(_onCouponRemoved);
    on<OrderPlaced>(_onOrderPlaced);
    on<CheckoutReset>(_onCheckoutReset);
  }

  Future<void> _onCheckoutInitialized(
    CheckoutInitialized event,
    Emitter<CheckoutState> emit,
  ) async {
    emit(CheckoutLoading());

    try {
      // Calculate initial totals
      final subtotal = event.cart.total;
      final tax = _calculateTax(subtotal);
      final shipping = _calculateShipping(subtotal, event.cart);
      final total = subtotal + tax + shipping;

      final checkoutState = CheckoutLoaded(
        cart: event.cart,
        subtotal: subtotal,
        tax: tax,
        shipping: shipping,
        total: total,
      );

      emit(checkoutState);

      // Auto-load wallets for each currency/type combination
      final cartGroups = checkoutState.cartByWallet;
      for (final entry in cartGroups.entries) {
        final group = entry.value;
        add(WalletsLoaded(
          walletType: group.walletType.name,
          currency: group.currency,
        ));
      }
    } catch (e) {
      emit(CheckoutError(UnknownFailure(e.toString())));
    }
  }

  Future<void> _onWalletsLoaded(
    WalletsLoaded event,
    Emitter<CheckoutState> emit,
  ) async {
    if (state is! CheckoutLoaded) return;

    final currentState = state as CheckoutLoaded;
    final walletKey = '${event.walletType}-${event.currency}';

    // Set loading state for this wallet
    emit(currentState.copyWith(
      walletLoading: {
        ...currentState.walletLoading,
        walletKey: true,
      },
    ));

    // Mock wallet creation (replace with actual wallet fetching)
    try {
      await Future.delayed(const Duration(milliseconds: 500));

      final walletOption = WalletOption(
        id: '${event.walletType}_${event.currency}_wallet',
        name: '${event.currency} Wallet',
        type: event.walletType,
        currency: event.currency,
        balance: 1000.0, // Mock balance
      );

      emit(currentState.copyWith(
        walletLoading: {
          ...currentState.walletLoading,
          walletKey: false,
        },
        availableWallets: {
          ...currentState.availableWallets,
          walletKey: [walletOption],
        },
        selectedWallets: {
          ...currentState.selectedWallets,
          walletKey: walletOption.id, // Auto-select if only option
        },
      ));
    } catch (e) {
      emit(currentState.copyWith(
        walletLoading: {
          ...currentState.walletLoading,
          walletKey: false,
        },
        walletErrors: {
          ...currentState.walletErrors,
          walletKey:
              'No ${event.walletType} wallet with ${event.currency} found',
        },
      ));
    }
  }

  void _onWalletSelected(
    WalletSelected event,
    Emitter<CheckoutState> emit,
  ) {
    if (state is! CheckoutLoaded) return;

    final currentState = state as CheckoutLoaded;

    emit(currentState.copyWith(
      selectedWallets: {
        ...currentState.selectedWallets,
        event.walletKey: event.walletId,
      },
    ));
  }

  void _onShippingAddressUpdated(
    ShippingAddressUpdated event,
    Emitter<CheckoutState> emit,
  ) {
    if (state is! CheckoutLoaded) return;

    final currentState = state as CheckoutLoaded;

    emit(currentState.copyWith(
      shippingAddress: event.address,
    ));
  }

  void _onShippingMethodSelected(
    ShippingMethodSelected event,
    Emitter<CheckoutState> emit,
  ) {
    if (state is! CheckoutLoaded) return;

    final currentState = state as CheckoutLoaded;

    // Recalculate totals with new shipping cost
    final subtotal = currentState.subtotal;
    final tax = currentState.tax;
    final shipping = event.method.cost;
    final discount = currentState.discount;
    final total = subtotal + tax + shipping - discount;

    emit(currentState.copyWith(
      shippingMethod: event.method,
      shipping: shipping,
      total: total,
    ));
  }

  Future<void> _onCouponApplied(
    CouponApplied event,
    Emitter<CheckoutState> emit,
  ) async {
    if (state is! CheckoutLoaded) return;

    final currentState = state as CheckoutLoaded;

    // TODO: Implement coupon validation API call
    // For now, mock a discount
    const mockDiscount = DiscountData(
      id: 'mock',
      code: 'DISCOUNT10',
      type: 'PERCENTAGE',
      value: 10.0,
      message: '10% discount applied!',
    );

    final discountAmount = currentState.subtotal * (mockDiscount.value / 100);
    final total = currentState.subtotal +
        currentState.tax +
        currentState.shipping -
        discountAmount;

    emit(currentState.copyWith(
      appliedDiscount: mockDiscount,
      discount: discountAmount,
      total: total,
    ));
  }

  void _onCouponRemoved(
    CouponRemoved event,
    Emitter<CheckoutState> emit,
  ) {
    if (state is! CheckoutLoaded) return;

    final currentState = state as CheckoutLoaded;
    final total =
        currentState.subtotal + currentState.tax + currentState.shipping;

    emit(currentState.copyWith(
      appliedDiscount: null,
      discount: 0.0,
      total: total,
    ));
  }

  Future<void> _onOrderPlaced(
    OrderPlaced event,
    Emitter<CheckoutState> emit,
  ) async {
    if (state is! CheckoutLoaded) return;

    final currentState = state as CheckoutLoaded;

    emit(CheckoutProcessing());

    try {
      // Validate all required data
      if (!currentState.isWalletSelectionComplete) {
        emit(CheckoutError(
            ValidationFailure('Please select wallets for all items')));
        return;
      }

      if (currentState.hasWalletErrors) {
        emit(
            CheckoutError(ValidationFailure('Wallet validation errors found')));
        return;
      }

      if (currentState.hasPhysicalProducts &&
          currentState.shippingAddress == null) {
        emit(CheckoutError(ValidationFailure(
            'Shipping address required for physical products')));
        return;
      }

      // Place the order
      final result = await _placeOrderUseCase(PlaceOrderParams(
        items: currentState.cart.items,
        totalAmount: currentState.total,
        currency: currentState.cart.currency,
        shippingAddressId: currentState.shippingAddress?.id,
        shippingMethodId: currentState.shippingMethod?.id,
        paymentMethod: 'wallet',
      ));

      result.fold(
        (failure) => emit(CheckoutError(failure)),
        (order) => emit(CheckoutSuccess(order)),
      );
    } catch (e) {
      emit(CheckoutError(UnknownFailure(e.toString())));
    }
  }

  void _onCheckoutReset(
    CheckoutReset event,
    Emitter<CheckoutState> emit,
  ) {
    emit(CheckoutInitial());
  }

  double _calculateTax(double subtotal) {
    // Mock tax calculation - 8% tax
    return subtotal * 0.08;
  }

  double _calculateShipping(double subtotal, dynamic cart) {
    // Check if cart has physical products
    final hasPhysicalProducts =
        cart.items.any((item) => item.product.type == ProductType.physical);

    if (!hasPhysicalProducts) return 0.0;

    // Free shipping for orders over $50
    if (subtotal >= 50.0) return 0.0;

    // Otherwise $5 shipping
    return 5.0;
  }
}
