module Billing
  module Stripe
    class ScheduleDowngrade
      def self.call(club:, plan_slug:, interval:)
        new(club: club, plan_slug: plan_slug, interval: interval).call
      end

      def initialize(club:, plan_slug:, interval:)
        @club = club
        @plan_slug = plan_slug.to_s
        @interval = PriceCatalog.normalize_interval(interval)
        @sub = club.subscription
      end

      def call
        release_existing_schedule!

        stripe_sub = ::Stripe::Subscription.retrieve(@sub.stripe_subscription_id)
        period_start, period_end = SubscriptionPeriod.times(stripe_sub)
        raise "Periodo di fatturazione non disponibile" if period_end.blank?

        current_item = stripe_sub.items.data.first
        new_price_id = PriceCatalog.price_id(plan_slug: @plan_slug, interval: @interval)

        schedule = ::Stripe::SubscriptionSchedule.create(from_subscription: stripe_sub.id)
        ::Stripe::SubscriptionSchedule.update(
          schedule.id,
          {
            end_behavior: "release",
            phases: [
              {
                items: [{ price: current_item.price.id, quantity: 1 }],
                start_date: schedule_phase_start(stripe_sub, period_start),
                end_date: period_end.to_i
              },
              {
                items: [{ price: new_price_id, quantity: 1 }],
                metadata: {
                  club_id: @club.id,
                  plan_slug: @plan_slug,
                  billing_interval: @interval
                }
              }
            ]
          }
        )

        pending_plan = Plan[@plan_slug]
        @sub.update!(
          pending_plan: pending_plan,
          pending_billing_interval: @interval,
          stripe_schedule_id: schedule.id
        )

        ChangePlanResult.new(
          kind: :downgrade,
          plan_slug: @plan_slug,
          interval: @interval,
          effective_at: period_end
        )
      end

      private

      def release_existing_schedule!
        return if @sub.stripe_schedule_id.blank?

        ::Stripe::SubscriptionSchedule.release(@sub.stripe_schedule_id)
      rescue ::Stripe::InvalidRequestError
        nil
      ensure
        @sub.update!(stripe_schedule_id: nil, pending_plan_id: nil, pending_billing_interval: nil)
      end

      def schedule_phase_start(stripe_sub, period_start)
        period_start&.to_i || stripe_sub.items.data.first.current_period_start
      end
    end
  end
end
