module Billing
  module Stripe
    # Allinea DB locale con Stripe dopo Checkout o webhook (idempotente).
    class CheckoutSync
      def self.call(club:, session_id: nil)
        new(club: club, session_id: session_id).call
      end

      def self.from_session(session)
        new.apply_from_session(session)
      end

      def self.from_subscription(stripe_sub, club: nil, plan_slug: nil, billing_interval: nil)
        new.apply_from_subscription(stripe_sub, club: club, plan_slug: plan_slug, billing_interval: billing_interval)
      end

      def initialize(club: nil, session_id: nil)
        @club = club
        @session_id = session_id
      end

      def call
        return false unless MatchLiveTv.stripe_enabled?

        if @session_id.present?
          session = ::Stripe::Checkout::Session.retrieve(@session_id)
          return false unless session_paid?(session)

          apply_from_session(session)
          return true
        end

        sync_from_customer
      end

      def apply_from_session(session)
        club = resolve_club(session) || @club
        return unless club

        sub = club.subscription || club.build_subscription
        sub.update!(stripe_customer_id: session.customer) if session.customer.present?

        return unless session.subscription.present?

        stripe_sub = ::Stripe::Subscription.retrieve(session.subscription)
        plan_slug = metadata_plan_slug(session) || "premium_light"
        plan_slug = "premium_full" if plan_slug == "premium"
        apply_from_subscription(
          stripe_sub,
          club: club,
          plan_slug: plan_slug,
          billing_interval: metadata_billing_interval(session)
        )
      end

      def apply_from_subscription(stripe_sub, club: nil, plan_slug: nil, billing_interval: nil)
        club ||= Club.joins(:subscription)
                     .find_by(subscriptions: { stripe_subscription_id: stripe_sub.id })
        club ||= resolve_club(stripe_sub)
        return unless club

        plan_slug ||= metadata_plan_slug(stripe_sub) || infer_plan_slug_from_price(stripe_sub) || "premium_light"
        plan_slug = "premium_full" if plan_slug == "premium"
        plan = Plan[plan_slug]
        status = map_status(stripe_sub.status)
        period_start, period_end = SubscriptionPeriod.times(stripe_sub)

        billing_interval = billing_interval.presence || metadata_billing_interval(stripe_sub) ||
                           infer_interval_from_price(stripe_sub) ||
                           PriceCatalog::DEFAULT_INTERVAL

        Billing::AssignPlan.call(
          club: club,
          plan_slug: plan.slug,
          status: 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: billing_interval
          }
        )
      end

      private

      def sync_from_customer
        return false unless @club

        customer_id = @club.subscription&.stripe_customer_id
        return false if customer_id.blank?

        stripe_sub = latest_active_subscription_for_club(customer_id, @club.id)
        return false unless stripe_sub

        apply_from_subscription(stripe_sub, club: @club)
        true
      end

      def latest_active_subscription_for_club(customer_id, club_id)
        list = ::Stripe::Subscription.list(customer: customer_id, status: "all", limit: 20)
        list.data
            .select { |s| %w[active trialing].include?(s.status) }
            .select { |s| subscription_belongs_to_club?(s, club_id) }
            .max_by(&:created)
      end

      def subscription_belongs_to_club?(stripe_sub, club_id)
        meta_club = metadata_club_id(stripe_sub)
        return meta_club == club_id if meta_club.present?

        team_id = metadata_team_id(stripe_sub)
        return Team.find_by(id: team_id)&.club_id == club_id if team_id.present?

        false
      end

      def session_paid?(session)
        session.payment_status == "paid" || session.status == "complete"
      end

      def infer_plan_slug_from_price(stripe_sub)
        price_id = stripe_sub.items&.data&.first&.price&.id
        return "premium_light" if price_id == MatchLiveTv.stripe_premium_light_monthly_price_id
        return "premium_light" if price_id == MatchLiveTv.stripe_premium_light_yearly_price_id
        return "premium_full" if price_id == MatchLiveTv.stripe_premium_full_monthly_price_id
        return "premium_full" if price_id == MatchLiveTv.stripe_premium_full_yearly_price_id

        "premium_light"
      end

      def infer_interval_from_price(stripe_sub)
        interval = stripe_sub.items&.data&.first&.price&.recurring&.interval
        return "monthly" if interval == "month"
        return "yearly" if interval == "year"

        nil
      end

      def resolve_club(obj)
        club_id = metadata_club_id(obj)
        return Club.find_by(id: club_id) if club_id.present?

        team_id = metadata_team_id(obj)
        return Team.find_by(id: team_id)&.club if team_id.present?

        @club
      end

      def metadata_billing_interval(obj)
        meta = obj.metadata
        return nil if meta.blank?

        val = meta["billing_interval"] || meta[:billing_interval]
        return nil if val.blank?

        PriceCatalog.normalize_interval(val)
      rescue ArgumentError
        nil
      end

      def metadata_plan_slug(obj)
        meta = obj.metadata
        return nil if meta.blank?

        meta["plan_slug"] || meta[:plan_slug]
      end

      def metadata_club_id(obj)
        meta = obj.metadata
        return nil if meta.blank?

        meta["club_id"] || meta[:club_id]
      end

      def metadata_team_id(obj)
        meta = obj.metadata
        return nil if meta.blank?

        meta["team_id"] || meta[:team_id]
      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
