module Billing
  module Stripe
    class ChangePlan
      VALID_PLANS = CheckoutSession::VALID_PLANS.freeze

      def self.call(club:, plan_slug:, interval: nil)
        new(club: club, plan_slug: plan_slug, interval: interval).call
      end

      def initialize(club:, plan_slug:, interval: nil)
        @club = club
        @plan_slug = plan_slug.to_s
        @interval = interval
      end

      def call
        raise "Stripe non configurato" unless MatchLiveTv.stripe_enabled?
        raise "Piano non valido" unless VALID_PLANS.include?(@plan_slug)

        sub = @club.subscription
        raise "Nessun abbonamento Stripe attivo" if sub.blank? || sub.stripe_subscription_id.blank?

        interval = resolve_interval(sub)
        if sub.plan.slug == @plan_slug && sub.billing_interval == interval
          raise "Sei già su questo piano e intervallo di fatturazione"
        end

        price_id = PriceCatalog.price_id(plan_slug: @plan_slug, interval: interval)

        stripe_sub = ::Stripe::Subscription.retrieve(sub.stripe_subscription_id)
        item_id = stripe_sub.items.data.first.id

        updated = ::Stripe::Subscription.update(
          stripe_sub.id,
          items: [{ id: item_id, price: price_id }],
          metadata: { club_id: @club.id, plan_slug: @plan_slug, billing_interval: interval },
          proration_behavior: "create_prorations"
        )

        apply_local_plan!(updated, interval: interval)
        updated
      end

      private

      def resolve_interval(sub)
        return PriceCatalog.normalize_interval(@interval) if @interval.present?

        sub.billing_interval.presence || PriceCatalog::DEFAULT_INTERVAL
      end

      def apply_local_plan!(stripe_sub, interval:)
        period_start, period_end = SubscriptionPeriod.times(stripe_sub)
        Billing::AssignPlan.call(
          club: @club,
          plan_slug: @plan_slug,
          status: map_status(stripe_sub.status),
          stripe_attrs: {
            stripe_customer_id: stripe_sub.customer,
            stripe_subscription_id: stripe_sub.id,
            current_period_start: period_start,
            current_period_end: period_end,
            cancel_at_period_end: stripe_sub.cancel_at_period_end,
            billing_interval: interval
          }
        )
      end

      def map_status(stripe_status)
        case stripe_status
        when "trialing" then "trialing"
        when "active" then "active"
        when "past_due", "unpaid" then "past_due"
        when "canceled", "incomplete_expired" then "canceled"
        else "incomplete"
        end
      end
    end
  end
end
