import 'package:adyen_checkout/adyen_checkout.dart';
import 'package:comwell_key_app/base/base_cubit.dart';
import 'package:comwell_key_app/check_out/bloc/check_out_state.dart';
import 'package:comwell_key_app/check_out/check_out_repository.dart';
import 'package:comwell_key_app/check_out/pages/check_out_page.dart';
import 'package:comwell_key_app/domain/repositories/booking_details_repository.dart';
import 'package:comwell_key_app/domain/repositories/pregistration_repository.dart';
import 'package:comwell_key_app/overview/models/booking.dart';
import 'package:comwell_key_app/domain/repositories/profile_repository.dart';
import 'package:comwell_key_app/routing/app_routes.dart';
import 'package:comwell_key_app/services/models/booking_dto.dart';
import 'package:comwell_key_app/tracking/comwell_tracking.dart';
import 'package:comwell_key_app/tracking/models/analytics_event_item.dart';
import 'package:comwell_key_app/utils/locator.dart';
import 'package:comwell_key_app/utils/urls.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:payment_plugin/presentation/app/bloc/payment_cubit.dart';
import 'package:url_launcher/url_launcher.dart';

class CheckoutCubit extends BaseCubit<CheckoutState> {
  final ProfileRepository profileRepository = locator<ProfileRepository>();
  final PreregistrationRepository preregistrationRepository = locator<PreregistrationRepository>();
  final BookingDetailsRepository bookingDetailsRepository = locator<BookingDetailsRepository>();
  final CheckOutRepository checkOutRepository;
  final _tracking = locator<ComwellTracking>();
  final pageController = PageController();
  bool _isAnimating = false;
  late Booking booking;
  final PaymentCubit paymentServicesCubit;

  CheckoutPage get currentPage => CheckoutPage.fromIndex(pageController.page?.toInt() ?? 0);

  CheckoutCubit(this.booking, this.checkOutRepository, this.paymentServicesCubit)
    : super(CheckoutState.initial(booking.balance ?? 0));

  void init() async {
    try {
      //TODO: Fetch club points eventually
      safeEmit(state.loading());
      final user = await profileRepository.fetchProfileSettings();
      safeEmit(state.clubPointsFetched(user.points));
      booking = await bookingDetailsRepository.getRemoteBookingDetails(
        booking.confirmationNumber,
        booking.hotelCode,
      );
      setItems(booking.addOnItems ?? []);
      safeEmit(state.loaded());
    } catch (e, st) {
      logError(e, st);
      safeEmit(state.checkoutError());
    }
  }

  void onApplyClubPointsClicked(bool value) {
    if (value) {
      safeEmit(state.clubPointsApplied());
    } else {
      safeEmit(state.clubPointsRemoved());
    }
  }

  void setItems(Iterable<BookingAddonItem> items) {
    safeEmit(state.itemsUpdated(items));
  }

  Future<void> processPayment() async {
    final analyticsEventItem = AnalyticsEventItem(
      hotelName: booking.hotelName,
      currency: "DKK",
      value: booking.balance?.toInt() ?? 0,
      placement: "check-out",
      itemIds: booking.addOnItemCodes,
      items: booking.addOnItemDescriptions,
      price: totalAddonPrice,
      quantity: booking.addOnItems?.length ?? 0,
      bookingReference: booking.confirmationNumber,
    );
    _tracking.trackBeginCheckout(analyticsEventItem);
    try {
      await paymentServicesCubit.createSession(
        booking.balance?.toInt() ?? 0,
        booking.confirmationNumber,
        state.applyClubPoints,
        booking.hotelCode,
      );
      await Future<void>.delayed(const Duration(milliseconds: 4000));
    } catch (e, st) {
      logError(e, st);
      safeEmit(state.checkoutError());
    }
  }

  Future<void> processCheckoutWithoutPaying() async {
    try {
      checkOut();
    } catch (e, st) {
      logError(e, st);
      await Future<void>.delayed(const Duration(milliseconds: 1000));
    }
  }

  void onContinueClicked(BuildContext context) {
    if (_isAnimating) return;
    _isAnimating = true;
    switch (currentPage) {
      case CheckoutPage.confirmation:
        if (booking.balance == 0 || booking.balance == null) {
          safeEmit(state.paymentProcessingNotNeeded());
          return _navigateToProcessing(context);
        }
        return _navigateTo(CheckoutPage.payment);
      case CheckoutPage.payment:
        return _navigateToProcessing(context);
    }
  }

  void _navigateToProcessing(BuildContext context) {
    _isAnimating = false;
    if (!state.isTermsAccepted && (booking.balance ?? 0.0) > 0.0) {
      safeEmit(state.showAcceptTermsError());
    } else {
      if (booking.balance == 0 || booking.balance == null) {
        processCheckoutWithoutPaying();
      } else {
        context.push(AppRoutes.paymentProcessing);
        processPayment();
      }
    }
  }

  bool onBackPressed() {
    if (pageController.page == 0.0) return false;
    if (_isAnimating) return true;
    _isAnimating = true;
    pageController
        .previousPage(duration: const Duration(milliseconds: 500), curve: Curves.fastOutSlowIn)
        .then((_) {
          _isAnimating = false;
          safeEmit(state.pageChanged(currentPage));
        });
    return true;
  }

  void _navigateTo(CheckoutPage page) async {
    await pageController.animateToPage(
      page.index,
      duration: const Duration(milliseconds: 500),
      curve: Curves.fastOutSlowIn,
    );
    safeEmit(state.pageChanged(page));
    _isAnimating = false;
  }

  @override
  Future<void> close() {
    pageController.dispose();
    return super.close();
  }

  void onAcceptTermsChanged(bool value) {
    if (value) {
      safeEmit(state.termsAccepted());
    } else {
      safeEmit(state.termsDenied());
    }
  }

  Future<void> checkOut() async {
    try {
      booking = await bookingDetailsRepository.getRemoteBookingDetails(
        booking.confirmationNumber,
        booking.hotelCode,
      );

      await checkOutRepository.checkOut(booking.confirmationNumber);

      safeEmit(state.checkoutSuccess());
    } catch (e, st) {
      logError(e, st);
      safeEmit(state.checkoutError());
      await Future<void>.delayed(const Duration(milliseconds: 1000));
    }
  }

  Future<void> onPaymentResult(PaymentResult result) async {
    switch (result) {
      case PaymentAdvancedFinished():
      case PaymentSessionFinished():
        //This is here to add time so that the payment is represented in the BookingDetails
        await Future<void>.delayed(const Duration(milliseconds: 2000));

        break;
      case PaymentCancelledByUser():
      case PaymentError():
    }
  }

  void showTermsAndConditions() {
    launchUrl(Uri.parse(ComwellUrls.termsAndConditions));
  }

  //Removes the decimal point from the balance if it is .0
  int get trimmedBalance {
    final balance = booking.balance;
    if (balance == null) return 0;
    if (balance == balance.toInt()) {
      return balance.toInt();
    } else {
      return balance.toInt();
    }
  }

  int get totalAddonPrice {
    return booking.addOnItems?.fold(0, (total, item) => total! + item.price) ?? 0;
  }
}