import { DomainEvent } from '../../events/domain-event.base';
import { ReportType } from '../../value-objects/report-type.enum';
import { ReportSchedule } from '../../value-objects/report-schedule.vo';
import { OutputFormat } from '../../value-objects/output-format.enum';

export class ReportDefinition {
  private _id: bigint | null = null;
  private readonly _reportType: ReportType;
  private _reportName: string;
  private readonly _reportCode: string;
  private _description: string;
  private _parameters: Record<string, any>;
  private _schedule: ReportSchedule | null;
  private _outputFormats: OutputFormat[];
  private _isActive: boolean;
  private readonly _createdAt: Date;
  private _lastGeneratedAt: Date | null;

  private _domainEvents: DomainEvent[] = [];

  private constructor(
    reportType: ReportType,
    reportName: string,
    reportCode: string,
    description: string,
    parameters: Record<string, any>,
    schedule: ReportSchedule | null,
    outputFormats: OutputFormat[],
    isActive: boolean,
    createdAt: Date,
    lastGeneratedAt: Date | null,
  ) {
    this._reportType = reportType;
    this._reportName = reportName;
    this._reportCode = reportCode;
    this._description = description;
    this._parameters = parameters;
    this._schedule = schedule;
    this._outputFormats = outputFormats;
    this._isActive = isActive;
    this._createdAt = createdAt;
    this._lastGeneratedAt = lastGeneratedAt;

    this.validateInvariants();
  }

  static create(params: {
    reportType: ReportType;
    reportName: string;
    reportCode: string;
    description?: string;
    parameters?: Record<string, any>;
    schedule?: ReportSchedule;
    outputFormats?: OutputFormat[];
  }): ReportDefinition {
    const outputFormats =
      params.outputFormats && params.outputFormats.length > 0
        ? params.outputFormats
        : [OutputFormat.EXCEL];

    return new ReportDefinition(
      params.reportType,
      params.reportName,
      params.reportCode,
      params.description || '',
      params.parameters || {},
      params.schedule || null,
      outputFormats,
      true,
      new Date(),
      null,
    );
  }

  static reconstitute(params: {
    id: bigint;
    reportType: ReportType;
    reportName: string;
    reportCode: string;
    description: string;
    parameters: Record<string, any>;
    schedule: ReportSchedule | null;
    outputFormats: OutputFormat[];
    isActive: boolean;
    createdAt: Date;
    lastGeneratedAt: Date | null;
  }): ReportDefinition {
    const definition = new ReportDefinition(
      params.reportType,
      params.reportName,
      params.reportCode,
      params.description,
      params.parameters,
      params.schedule,
      params.outputFormats,
      params.isActive,
      params.createdAt,
      params.lastGeneratedAt,
    );
    definition._id = params.id;
    return definition;
  }

  private validateInvariants(): void {
    if (this._outputFormats.length === 0) {
      throw new Error('报表定义至少需要支持一种输出格式');
    }

    if (this._schedule?.enabled && !this._schedule.cronExpression) {
      throw new Error('启用调度时必须有有效的 cron 表达式');
    }
  }

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

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

  get reportName(): string {
    return this._reportName;
  }

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

  get description(): string {
    return this._description;
  }

  get parameters(): Record<string, any> {
    return { ...this._parameters };
  }

  get schedule(): ReportSchedule | null {
    return this._schedule;
  }

  get outputFormats(): OutputFormat[] {
    return [...this._outputFormats];
  }

  get isActive(): boolean {
    return this._isActive;
  }

  get createdAt(): Date {
    return this._createdAt;
  }

  get lastGeneratedAt(): Date | null {
    return this._lastGeneratedAt;
  }

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

  // Commands
  updateName(name: string): void {
    this._reportName = name;
  }

  updateDescription(description: string): void {
    this._description = description;
  }

  updateParameters(parameters: Record<string, any>): void {
    this._parameters = parameters;
  }

  setSchedule(schedule: ReportSchedule): void {
    this._schedule = schedule;
    this.validateInvariants();
  }

  enableSchedule(): void {
    if (this._schedule) {
      this._schedule = this._schedule.enable();
    }
  }

  disableSchedule(): void {
    if (this._schedule) {
      this._schedule = this._schedule.disable();
    }
  }

  addOutputFormat(format: OutputFormat): void {
    if (!this._outputFormats.includes(format)) {
      this._outputFormats.push(format);
    }
  }

  removeOutputFormat(format: OutputFormat): void {
    if (this._outputFormats.length <= 1) {
      throw new Error('报表定义至少需要支持一种输出格式');
    }
    this._outputFormats = this._outputFormats.filter((f) => f !== format);
  }

  activate(): void {
    this._isActive = true;
  }

  deactivate(): void {
    this._isActive = false;
  }

  markGenerated(): void {
    this._lastGeneratedAt = new Date();
  }

  supportsFormat(format: OutputFormat): boolean {
    return this._outputFormats.includes(format);
  }

  isScheduled(): boolean {
    return this._schedule !== null && this._schedule.enabled;
  }

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

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