import { Result } from '../value-objects/Result';

export type SubscriptionStatus = 'active' | 'canceled' | 'past_due' | 'trialing' | 'incomplete';

export interface SubscriptionProps {
  id: string;
  userId: string;
  stripeSubscriptionId: string;
  stripePriceId: string;
  stripeProductId: string;
  status: SubscriptionStatus;
  currentPeriodStart: Date;
  currentPeriodEnd: Date;
  cancelAtPeriodEnd: boolean;
  canceledAt?: Date;
  createdAt: Date;
  updatedAt: Date;
}

export class Subscription {
  private constructor(private props: SubscriptionProps) {}

  static create(props: {
    id: string;
    userId: string;
    stripeSubscriptionId: string;
    stripePriceId: string;
    stripeProductId: string;
    status: SubscriptionStatus;
    currentPeriodStart: Date;
    currentPeriodEnd: Date;
    cancelAtPeriodEnd?: boolean;
    canceledAt?: Date;
    createdAt?: Date;
    updatedAt?: Date;
  }): Result<Subscription> {
    const validation = this.validatePeriod(props.currentPeriodStart, props.currentPeriodEnd);
    if (validation.isFailure()) {
      return Result.fail(validation.getError());
    }

    const subscription = this.buildSubscription(props);
    return Result.ok(subscription);
  }

  private static validatePeriod(start: Date, end: Date): Result<void> {
    if (start >= end) {
      return Result.fail('Current period start must be before period end');
    }
    return Result.ok(undefined);
  }

  private static buildSubscription(props: any): Subscription {
    return new Subscription({
      ...props,
      cancelAtPeriodEnd: props.cancelAtPeriodEnd ?? false,
      createdAt: props.createdAt || new Date(),
      updatedAt: props.updatedAt || new Date(),
    });
  }

  getId(): string {
    return this.props.id;
  }

  getUserId(): string {
    return this.props.userId;
  }

  getStripeSubscriptionId(): string {
    return this.props.stripeSubscriptionId;
  }

  getStatus(): SubscriptionStatus {
    return this.props.status;
  }

  isActive(): boolean {
    return this.props.status === 'active' || this.props.status === 'trialing';
  }

  isCanceled(): boolean {
    return this.props.status === 'canceled';
  }

  willCancelAtPeriodEnd(): boolean {
    return this.props.cancelAtPeriodEnd;
  }

  scheduleForCancellation(): Result<void> {
    if (!this.isActive()) {
      return Result.fail('Cannot cancel inactive subscription');
    }

    this.props.cancelAtPeriodEnd = true;
    this.props.updatedAt = new Date();

    return Result.ok(undefined);
  }

  reactivate(): Result<void> {
    if (!this.props.cancelAtPeriodEnd) {
      return Result.fail('Subscription is not scheduled for cancellation');
    }

    this.props.cancelAtPeriodEnd = false;
    this.props.updatedAt = new Date();

    return Result.ok(undefined);
  }

  cancel(): Result<void> {
    if (this.isCanceled()) {
      return Result.fail('Subscription is already canceled');
    }

    this.props.status = 'canceled';
    this.props.canceledAt = new Date();
    this.props.updatedAt = new Date();

    return Result.ok(undefined);
  }

  updateStatus(status: SubscriptionStatus): void {
    this.props.status = status;
    this.props.updatedAt = new Date();
  }

  updatePeriod(start: Date, end: Date): Result<void> {
    if (start >= end) {
      return Result.fail('Period start must be before period end');
    }

    this.props.currentPeriodStart = start;
    this.props.currentPeriodEnd = end;
    this.props.updatedAt = new Date();

    return Result.ok(undefined);
  }

  toPlainObject() {
    return {
      id: this.props.id,
      userId: this.props.userId,
      stripeSubscriptionId: this.props.stripeSubscriptionId,
      stripePriceId: this.props.stripePriceId,
      stripeProductId: this.props.stripeProductId,
      status: this.props.status,
      currentPeriodStart: this.props.currentPeriodStart,
      currentPeriodEnd: this.props.currentPeriodEnd,
      cancelAtPeriodEnd: this.props.cancelAtPeriodEnd,
      canceledAt: this.props.canceledAt,
      createdAt: this.props.createdAt,
      updatedAt: this.props.updatedAt,
    };
  }
}
