import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart' show PlatformException;
import 'package:kasy_kit/core/data/api/analytics_api.dart';
import 'package:kasy_kit/core/data/models/subscription.dart';
import 'package:kasy_kit/core/states/models/user_state.dart';
import 'package:kasy_kit/core/states/translations.dart';
import 'package:kasy_kit/core/states/user_state_notifier.dart';
import 'package:kasy_kit/core/toast/toast_service.dart';
import 'package:kasy_kit/features/feedbacks/repositories/feature_request_repository.dart';
import 'package:kasy_kit/features/subscription/api/inapp_subscription_api.dart'
    show UserCancelledPurchaseException;
import 'package:kasy_kit/features/subscription/providers/models/premium_state.dart';
import 'package:kasy_kit/features/subscription/repositories/subscription_repository.dart';
import 'package:kasy_kit/router.dart';
import 'package:logger/logger.dart';
import 'package:purchases_flutter/purchases_flutter.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:sentry_flutter/sentry_flutter.dart';

part 'premium_page_provider.g.dart';

@Riverpod()
class PremiumStateNotifier extends _$PremiumStateNotifier {
  final Logger _logger = Logger();

  UserState get _userState => ref.read(userStateNotifierProvider);

  SubscriptionRepository get _subscriptionRepository =>
      ref.read(subscriptionRepositoryProvider);

  AnalyticsApi? get _analyticsApi => ref.read(analyticsApiProvider);

  @override
  Future<PremiumState> build() async {
    try {
      // If you have installed the remote config brick
      // you can use it like this
      // RemoteConfig is amazing to manage and test different settings for paywalls
      // ex: you can test different title and texts
      // final remoteConfigApi = ref.watch(remoteConfigApiProvider);
      // print(remoteConfigApi.subscription.title.value);
      final offers = await _subscriptionRepository.getOffers();
      if (offers.isEmpty) {
        _logger.w(
          'RevenueCat returned no subscription offers. The paywall will show '
          'an empty-products state instead of a blank screen.',
        );
        return const PremiumState(offers: []);
      }

      return switch (_userState.subscription) {
        SubscriptionStateData(:final activeOffer) => PremiumState.active(
          activeOffer: offers.firstWhere(
            (element) => element.skuId == activeOffer?.skuId,
            orElse: () => offers.first,
          ),
        ),
        SubscriptionInactiveStateData() => PremiumState(
          offers: offers,
          selectedOffer: offers.first,
        ),
        _ => PremiumState(offers: offers, selectedOffer: offers.first),
      };
    } catch (err, stack) {
      // RevenueCat CONFIGURATION_ERROR (code 23) means the products exist in the
      // dashboard but aren't live on the store yet (e.g. "Ready to Submit").
      // This is expected before the first app submission — skip Sentry noise.
      final isConfigError = err is PlatformException &&
          (err.details as Map?)?['readableErrorCode'] == 'CONFIGURATION_ERROR';
      if (isConfigError) {
        _logger.w(
          'RevenueCat CONFIGURATION_ERROR: products not yet live on App Store. '
          'Showing empty paywall state.',
        );
      } else {
        await Sentry.captureException(err, stackTrace: stack);
        _logger.e(
          '...PremiumStateNotifier: init failed, '
          '👉 Make sure you have properly setup subscription '
          'following our guide (https://kasy.dev/docs/monetize/subscription-template/) '
          '$err --> $stack',
          stackTrace: stack,
        );
      }
      return const PremiumStateData(offers: []);
    }
  }

  Future<void> startCoupon() async {
    if (TargetPlatform.iOS != defaultTargetPlatform) {
      return;
    }
    await Purchases.presentCodeRedemptionSheet();
  }

  void selectOffer(SubscriptionProduct offer) {
    if (state.value == null) {
      throw "cannot select offer";
    }
    final newState = switch (state.value!) {
      final PremiumStateData el => el.copyWith(selectedOffer: offer),
      final PremiumStateActive el => el.copyWith(activeOffer: offer),
      _ => null,
    };
    if (newState == null) {
      throw "cannot select offer";
    }
    state = AsyncValue.data(newState);
  }

  Future<void> purchase({String? paywall, String? redirectRoute}) async {
    if (state.value is! PremiumStateData) {
      throw "cannot purchase without offers";
    }
    final offer = state.value?.selectedOffer;
    if (offer == null) {
      throw "cannot purchase without selected offer";
    }
    await purchaseOffer(offer, paywall: paywall, redirectRoute: redirectRoute);
  }

  Future<void> purchaseOffer(
    SubscriptionProduct offer, {
    String? paywall,
    String? redirectRoute,
  }) async {
    state = switch (state.value) {
      PremiumStateData(:final offers) => AsyncData(
        PremiumState.sending(
          isPremium: false,
          offers: offers,
          selectedOffer: offer,
        ),
      ),
      _ => throw "cannot purchase while active",
    };
    try {
      final entitlements = await _subscriptionRepository.purchase(offer);
      state = AsyncData(PremiumState.active(activeOffer: offer));
      await _analyticsApi?.logEvent("purchase", {
        "skuId": offer.skuId,
        "price": offer.price,
        // "promotion": offer.promotion,
        "duration": offer.duration,
        "paywall": paywall,
      });
      // let's refresh the user state
      await ref
          .read(userStateNotifierProvider.notifier)
          .refreshSubscription(product: offer, entitlements: entitlements);
      final t = ref.read(translationsProvider);
      ref
          .read(toastProvider)
          .success(
            title: t.premium.purchase_success_title,
            text: t.premium.purchase_success_text,
          );
      await Future.delayed(const Duration(seconds: 2));
      if (redirectRoute != null) ref.read(goRouterProvider).go(redirectRoute);
    } catch (err, stackTrace) {
      state = switch (state.value) {
        PremiumStateData(:final offers) => AsyncData(
          PremiumState(offers: offers, selectedOffer: offer),
        ),
        PremiumStateSending(:final offers) => AsyncData(
          PremiumState(offers: offers, selectedOffer: offer),
        ),
        PremiumStateActive() => state,
        _ => throw "cannot purchase while active",
      };
      if (err is UserCancelledPurchaseException) return;
      await Sentry.captureException(err, stackTrace: stackTrace);
      _logger.e("...PremiumStateNotifier: purchase failed $err : $stackTrace");
      final t = ref.read(translationsProvider);
      ref
          .read(toastProvider)
          .error(
            title: t.premium.error_title,
            text: t.premium.error_text,
            reason: "We were unable to process your subscription",
          );
    }
  }

  Future<void> unsubscribe() async {
    // _analytics.logPageOpened("launch_unsubscribe_page");
    await _subscriptionRepository.unsubscribe();
  }

  Future<void> saveUnsubscribeReason(String reason) async {
    try {
      final userId = _userState.user.idOrNull;
      if (userId == null) return;
      await ref
          .read(featureRequestRepositoryProvider)
          .createNewFeatureSuggestion(
            userId: userId,
            title: "unsubscribed",
            description: reason,
          );
      _logger.i('👉 Unsubscribe reason saved successfully');
    } catch (err, stackTrace) {
      _logger.e('👉 Error saving unsubscribe reason: $err : $stackTrace');
      await Sentry.captureException(err, stackTrace: stackTrace);
      // Don't rethrow as we don't want to block the unsubscribe process
    }
  }

  Future<void> restore({String? redirectRoute}) async {
    if (state.value is! PremiumStateData) {
      throw "cannot restore while active";
    }
    final data = state.value! as PremiumStateData;

    // Only enter loading state when there are offers to display; with an empty
    // offer list the paywall already shows the empty-state UI.
    if (data.offers.isNotEmpty) {
      state = AsyncData(
        PremiumState.sending(
          isPremium: false,
          offers: data.offers,
          selectedOffer: data.selectedOffer,
        ),
      );
    }

    try {
      await _subscriptionRepository.restorePurchase();
      final t = ref.read(translationsProvider);
      ref
          .read(toastProvider)
          .success(
            title: t.premium.restore_success_title,
            text: t.premium.restore_success_text,
          );
      await Future.delayed(const Duration(seconds: 2));
      if (redirectRoute != null) ref.read(goRouterProvider).go(redirectRoute);
    } catch (err, trace) {
      _logger.e("Error while restoring purchase: $err : $trace");
      state = AsyncData(
        PremiumStateData(offers: data.offers, selectedOffer: data.selectedOffer),
      );
      final t = ref.read(translationsProvider);
      ref
          .read(toastProvider)
          .error(
            title: t.premium.error_title,
            text: t.premium.error_text,
            reason: "We were unable to restore your subscription",
          );
    }
  }
}

class SubscriptionException implements Exception {
  final String message;

  SubscriptionException(this.message);
}
