/**
 * Subscription Status Value Object
 *
 * Represents the different statuses a subscription can have.
 * Follows Stripe subscription lifecycle states.
 */

export type SubscriptionStatusType =
  | 'ACTIVE'
  | 'PAST_DUE'
  | 'CANCELED'
  | 'INCOMPLETE'
  | 'INCOMPLETE_EXPIRED'
  | 'TRIALING'
  | 'UNPAID'
  | 'PAUSED';

interface StatusDetails {
  readonly label: string;
  readonly description: string;
  readonly allowsAccess: boolean;
  readonly requiresAction: boolean;
}

const STATUS_DETAILS: Record<SubscriptionStatusType, StatusDetails> = {
  ACTIVE: {
    label: 'Active',
    description: 'Subscription is active and fully paid',
    allowsAccess: true,
    requiresAction: false,
  },
  PAST_DUE: {
    label: 'Past Due',
    description: 'Payment failed but subscription still active. Action required.',
    allowsAccess: true, // Grace period
    requiresAction: true,
  },
  CANCELED: {
    label: 'Canceled',
    description: 'Subscription has been canceled',
    allowsAccess: false,
    requiresAction: false,
  },
  INCOMPLETE: {
    label: 'Incomplete',
    description: 'Initial payment failed during subscription creation',
    allowsAccess: false,
    requiresAction: true,
  },
  INCOMPLETE_EXPIRED: {
    label: 'Incomplete Expired',
    description: 'Subscription creation payment window expired',
    allowsAccess: false,
    requiresAction: false,
  },
  TRIALING: {
    label: 'Trialing',
    description: 'Subscription is in trial period',
    allowsAccess: true,
    requiresAction: false,
  },
  UNPAID: {
    label: 'Unpaid',
    description: 'All payment retry attempts have failed',
    allowsAccess: false,
    requiresAction: true,
  },
  PAUSED: {
    label: 'Paused',
    description: 'Subscription has been paused',
    allowsAccess: false,
    requiresAction: false,
  },
};

// Status transitions that are valid
const VALID_TRANSITIONS: Record<SubscriptionStatusType, SubscriptionStatusType[]> = {
  ACTIVE: ['PAST_DUE', 'CANCELED', 'PAUSED'],
  PAST_DUE: ['ACTIVE', 'CANCELED', 'UNPAID'],
  CANCELED: [], // Terminal state
  INCOMPLETE: ['ACTIVE', 'INCOMPLETE_EXPIRED'],
  INCOMPLETE_EXPIRED: [], // Terminal state
  TRIALING: ['ACTIVE', 'PAST_DUE', 'CANCELED'],
  UNPAID: ['ACTIVE', 'CANCELED'],
  PAUSED: ['ACTIVE', 'CANCELED'],
};

export class SubscriptionStatus {
  private constructor(private readonly status: SubscriptionStatusType) {}

  static create(status: SubscriptionStatusType): SubscriptionStatus {
    if (!STATUS_DETAILS[status]) {
      throw new Error(`Invalid subscription status: ${status}`);
    }
    return new SubscriptionStatus(status);
  }

  static fromString(value: string): SubscriptionStatus {
    const upperValue = value.toUpperCase().replace(/-/g, '_') as SubscriptionStatusType;
    if (!STATUS_DETAILS[upperValue]) {
      throw new Error(`Invalid subscription status: ${value}`);
    }
    return new SubscriptionStatus(upperValue);
  }

  static fromStripeStatus(stripeStatus: string): SubscriptionStatus {
    // Map Stripe status to our internal status
    const mapping: Record<string, SubscriptionStatusType> = {
      active: 'ACTIVE',
      past_due: 'PAST_DUE',
      canceled: 'CANCELED',
      incomplete: 'INCOMPLETE',
      incomplete_expired: 'INCOMPLETE_EXPIRED',
      trialing: 'TRIALING',
      unpaid: 'UNPAID',
      paused: 'PAUSED',
    };

    const mappedStatus = mapping[stripeStatus.toLowerCase()];
    if (!mappedStatus) {
      throw new Error(`Unknown Stripe subscription status: ${stripeStatus}`);
    }
    return new SubscriptionStatus(mappedStatus);
  }

  static active(): SubscriptionStatus {
    return new SubscriptionStatus('ACTIVE');
  }

  static canceled(): SubscriptionStatus {
    return new SubscriptionStatus('CANCELED');
  }

  static pastDue(): SubscriptionStatus {
    return new SubscriptionStatus('PAST_DUE');
  }

  static trialing(): SubscriptionStatus {
    return new SubscriptionStatus('TRIALING');
  }

  get value(): SubscriptionStatusType {
    return this.status;
  }

  get label(): string {
    return STATUS_DETAILS[this.status].label;
  }

  get description(): string {
    return STATUS_DETAILS[this.status].description;
  }

  /**
   * Returns true if this status allows access to the platform
   */
  allowsAccess(): boolean {
    return STATUS_DETAILS[this.status].allowsAccess;
  }

  /**
   * Returns true if this status requires user action (e.g., update payment method)
   */
  requiresAction(): boolean {
    return STATUS_DETAILS[this.status].requiresAction;
  }

  /**
   * Returns true if this is a terminal state (cannot transition out)
   */
  isTerminal(): boolean {
    return VALID_TRANSITIONS[this.status].length === 0;
  }

  /**
   * Returns true if the subscription is in good standing
   */
  isInGoodStanding(): boolean {
    return this.status === 'ACTIVE' || this.status === 'TRIALING';
  }

  /**
   * Check if transition to new status is valid
   */
  canTransitionTo(newStatus: SubscriptionStatus): boolean {
    return VALID_TRANSITIONS[this.status].includes(newStatus.value);
  }

  /**
   * Transition to new status if valid
   */
  transitionTo(newStatus: SubscriptionStatus): SubscriptionStatus {
    if (!this.canTransitionTo(newStatus)) {
      throw new Error(`Invalid status transition from ${this.status} to ${newStatus.value}`);
    }
    return newStatus;
  }

  equals(other: SubscriptionStatus): boolean {
    return this.status === other.status;
  }

  toString(): string {
    return this.status;
  }

  /**
   * Convert to Stripe-compatible status string
   */
  toStripeStatus(): string {
    return this.status.toLowerCase().replace(/_/g, '-');
  }
}
