import { HttpStatus } from '@nestjs/common';
import {
  type PlanTier,
  type SubscriptionStatus,
} from '@prisma/client';
import { AggregateRoot, DomainException, ErrorCode, Result } from '@modules/shared';
import { SubscriptionCancelledEvent } from '../events/subscription-cancelled.event';
import { SubscriptionCreatedEvent } from '../events/subscription-created.event';
import { SubscriptionExpiredEvent } from '../events/subscription-expired.event';
import { SubscriptionRenewedEvent } from '../events/subscription-renewed.event';
import { SubscriptionUpgradedEvent } from '../events/subscription-upgraded.event';

export interface SubscriptionProps {
  userId: string;
  planId: string;
  planTier: PlanTier;
  status: SubscriptionStatus;
  currentPeriodStart: Date;
  currentPeriodEnd: Date;
  cancelledAt: Date | null;
}

export class SubscriptionEntity extends AggregateRoot<string> {
  private _userId: string;
  private _planId: string;
  private _planTier: PlanTier;
  private _status: SubscriptionStatus;
  private _currentPeriodStart: Date;
  private _currentPeriodEnd: Date;
  private _cancelledAt: Date | null;

  constructor(id: string, props: SubscriptionProps, createdAt?: Date, updatedAt?: Date) {
    super(id, createdAt, updatedAt);
    this._userId = props.userId;
    this._planId = props.planId;
    this._planTier = props.planTier;
    this._status = props.status;
    this._currentPeriodStart = props.currentPeriodStart;
    this._currentPeriodEnd = props.currentPeriodEnd;
    this._cancelledAt = props.cancelledAt;
  }

  get userId(): string { return this._userId; }
  get planId(): string { return this._planId; }
  get planTier(): PlanTier { return this._planTier; }
  get status(): SubscriptionStatus { return this._status; }
  get currentPeriodStart(): Date { return this._currentPeriodStart; }
  get currentPeriodEnd(): Date { return this._currentPeriodEnd; }
  get cancelledAt(): Date | null { return this._cancelledAt; }

  static createNew(
    id: string,
    userId: string,
    planId: string,
    planTier: PlanTier,
    periodStart: Date,
    periodEnd: Date,
  ): SubscriptionEntity {
    const subscription = new SubscriptionEntity(id, {
      userId,
      planId,
      planTier,
      status: 'ACTIVE',
      currentPeriodStart: periodStart,
      currentPeriodEnd: periodEnd,
      cancelledAt: null,
    });

    subscription.addDomainEvent(
      new SubscriptionCreatedEvent(id, userId, planTier),
    );

    return subscription;
  }

  upgrade(newPlanId: string, newPlanTier: PlanTier): Result<void, DomainException> {
    if (this._status !== 'ACTIVE') {
      return Result.err(
        new DomainException(
          ErrorCode.SUBSCRIPTION_INACTIVE,
          `Không thể nâng cấp subscription ở trạng thái ${this._status}`,
          HttpStatus.CONFLICT,
        ),
      );
    }

    const oldTier = this._planTier;
    this._planId = newPlanId;
    this._planTier = newPlanTier;
    this.updatedAt = new Date();

    this.addDomainEvent(
      new SubscriptionUpgradedEvent(this.id, this._userId, oldTier, newPlanTier),
    );
    return Result.ok(undefined);
  }

  cancel(): Result<void, DomainException> {
    if (this._status === 'CANCELLED') {
      return Result.err(
        new DomainException(
          ErrorCode.SUBSCRIPTION_ALREADY_CANCELLED,
          'Subscription đã bị hủy',
          HttpStatus.CONFLICT,
        ),
      );
    }

    this._status = 'CANCELLED';
    this._cancelledAt = new Date();
    this.updatedAt = new Date();

    this.addDomainEvent(
      new SubscriptionCancelledEvent(this.id, this._userId, this._planTier),
    );
    return Result.ok(undefined);
  }

  markExpired(): Result<void, DomainException> {
    if (this._status !== 'ACTIVE' && this._status !== 'PAST_DUE') {
      return Result.err(
        new DomainException(
          ErrorCode.SUBSCRIPTION_INACTIVE,
          `Không thể đánh dấu hết hạn ở trạng thái ${this._status}`,
          HttpStatus.CONFLICT,
        ),
      );
    }
    this._status = 'EXPIRED';
    this.updatedAt = new Date();

    this.addDomainEvent(
      new SubscriptionExpiredEvent(this.id, this._userId, this._planTier),
    );
    return Result.ok(undefined);
  }

  markPastDue(): Result<void, DomainException> {
    if (this._status !== 'ACTIVE') {
      return Result.err(
        new DomainException(
          ErrorCode.SUBSCRIPTION_INACTIVE,
          `Không thể đánh dấu quá hạn ở trạng thái ${this._status}`,
          HttpStatus.CONFLICT,
        ),
      );
    }
    this._status = 'PAST_DUE';
    this.updatedAt = new Date();
    return Result.ok(undefined);
  }

  renewPeriod(newStart: Date, newEnd: Date): void {
    this._currentPeriodStart = newStart;
    this._currentPeriodEnd = newEnd;
    this._status = 'ACTIVE';
    this.updatedAt = new Date();

    this.addDomainEvent(
      new SubscriptionRenewedEvent(this.id, this._userId, this._planTier, newStart, newEnd),
    );
  }

  isActive(): boolean {
    return this._status === 'ACTIVE';
  }

  isExpired(): boolean {
    return this._currentPeriodEnd < new Date();
  }
}
