import { DomainEvent } from '../../events/domain-event.base';
import { SnapshotCreatedEvent } from '../../events/snapshot-created.event';
import { ReportType } from '../../value-objects/report-type.enum';
import { ReportPeriod } from '../../value-objects/report-period.enum';
import { SnapshotData } from '../../value-objects/snapshot-data.vo';
import { DataSource } from '../../value-objects/data-source.vo';

export class ReportSnapshot {
  private _id: bigint | null = null;
  private readonly _reportType: ReportType;
  private readonly _reportCode: string;
  private readonly _reportPeriod: ReportPeriod;
  private readonly _periodKey: string;
  private _snapshotData: SnapshotData;
  private _dataSource: DataSource;
  private _filterParams: Record<string, any> | null;
  private readonly _periodStartAt: Date;
  private readonly _periodEndAt: Date;
  private readonly _generatedAt: Date;
  private _expiresAt: Date | null;

  private _domainEvents: DomainEvent[] = [];

  private constructor(
    reportType: ReportType,
    reportCode: string,
    reportPeriod: ReportPeriod,
    periodKey: string,
    snapshotData: SnapshotData,
    dataSource: DataSource,
    filterParams: Record<string, any> | null,
    periodStartAt: Date,
    periodEndAt: Date,
    generatedAt: Date,
    expiresAt: Date | null,
  ) {
    this._reportType = reportType;
    this._reportCode = reportCode;
    this._reportPeriod = reportPeriod;
    this._periodKey = periodKey;
    this._snapshotData = snapshotData;
    this._dataSource = dataSource;
    this._filterParams = filterParams;
    this._periodStartAt = periodStartAt;
    this._periodEndAt = periodEndAt;
    this._generatedAt = generatedAt;
    this._expiresAt = expiresAt;
  }

  static create(params: {
    reportType: ReportType;
    reportCode: string;
    reportPeriod: ReportPeriod;
    periodKey: string;
    snapshotData: SnapshotData;
    dataSource: DataSource;
    filterParams?: Record<string, any>;
    periodStartAt: Date;
    periodEndAt: Date;
    expiresAt?: Date;
  }): ReportSnapshot {
    const snapshot = new ReportSnapshot(
      params.reportType,
      params.reportCode,
      params.reportPeriod,
      params.periodKey,
      params.snapshotData,
      params.dataSource,
      params.filterParams || null,
      params.periodStartAt,
      params.periodEndAt,
      new Date(),
      params.expiresAt || null,
    );

    snapshot.addDomainEvent(
      new SnapshotCreatedEvent({
        snapshotId: params.periodKey,
        reportType: params.reportType,
        reportCode: params.reportCode,
        reportPeriod: params.reportPeriod,
        periodKey: params.periodKey,
        rowCount: params.snapshotData.getRowCount(),
        periodStartAt: params.periodStartAt,
        periodEndAt: params.periodEndAt,
      }),
    );

    return snapshot;
  }

  static reconstitute(params: {
    id: bigint;
    reportType: ReportType;
    reportCode: string;
    reportPeriod: ReportPeriod;
    periodKey: string;
    snapshotData: SnapshotData;
    dataSource: DataSource;
    filterParams: Record<string, any> | null;
    periodStartAt: Date;
    periodEndAt: Date;
    generatedAt: Date;
    expiresAt: Date | null;
  }): ReportSnapshot {
    const snapshot = new ReportSnapshot(
      params.reportType,
      params.reportCode,
      params.reportPeriod,
      params.periodKey,
      params.snapshotData,
      params.dataSource,
      params.filterParams,
      params.periodStartAt,
      params.periodEndAt,
      params.generatedAt,
      params.expiresAt,
    );
    snapshot._id = params.id;
    return snapshot;
  }

  // Getters
  get id(): bigint | null {
    return this._id;
  }

  get reportType(): ReportType {
    return this._reportType;
  }

  get reportCode(): string {
    return this._reportCode;
  }

  get reportPeriod(): ReportPeriod {
    return this._reportPeriod;
  }

  get periodKey(): string {
    return this._periodKey;
  }

  get snapshotData(): SnapshotData {
    return this._snapshotData;
  }

  get dataSource(): DataSource {
    return this._dataSource;
  }

  get filterParams(): Record<string, any> | null {
    return this._filterParams ? { ...this._filterParams } : null;
  }

  get periodStartAt(): Date {
    return this._periodStartAt;
  }

  get periodEndAt(): Date {
    return this._periodEndAt;
  }

  get generatedAt(): Date {
    return this._generatedAt;
  }

  get expiresAt(): Date | null {
    return this._expiresAt;
  }

  get rowCount(): number {
    return this._snapshotData.getRowCount();
  }

  get domainEvents(): DomainEvent[] {
    return [...this._domainEvents];
  }

  // Commands
  updateSnapshotData(data: SnapshotData): void {
    this._snapshotData = data;
  }

  setExpiration(expiresAt: Date): void {
    this._expiresAt = expiresAt;
  }

  isExpired(): boolean {
    if (!this._expiresAt) {
      return false;
    }
    return new Date() > this._expiresAt;
  }

  isFresh(maxAgeSeconds: number): boolean {
    return this._dataSource.isFresh(maxAgeSeconds);
  }

  clearDomainEvents(): void {
    this._domainEvents = [];
  }

  protected addDomainEvent(event: DomainEvent): void {
    this._domainEvents.push(event);
  }
}
