import { DomainError } from '../errors/domain.error';

export enum BackupShareStatus {
  ACTIVE = 'ACTIVE',
  REVOKED = 'REVOKED',
  ROTATED = 'ROTATED',
}

export interface BackupShareProps {
  shareId: bigint | null;
  userId: bigint;
  accountSequence: string;
  publicKey: string;
  partyIndex: number;
  threshold: number;
  totalParties: number;
  encryptedShareData: string;
  encryptionKeyId: string;
  status: BackupShareStatus;
  accessCount: number;
  lastAccessedAt: Date | null;
  createdAt: Date;
  updatedAt: Date;
  revokedAt: Date | null;
}

export class BackupShare {
  private _shareId: bigint | null;
  private readonly _userId: bigint;
  private readonly _accountSequence: string;
  private readonly _publicKey: string;
  private readonly _partyIndex: number;
  private readonly _threshold: number;
  private readonly _totalParties: number;
  private _encryptedShareData: string;
  private _encryptionKeyId: string;
  private _status: BackupShareStatus;
  private _accessCount: number;
  private _lastAccessedAt: Date | null;
  private readonly _createdAt: Date;
  private _updatedAt: Date;
  private _revokedAt: Date | null;

  private constructor(props: BackupShareProps) {
    this._shareId = props.shareId;
    this._userId = props.userId;
    this._accountSequence = props.accountSequence;
    this._publicKey = props.publicKey;
    this._partyIndex = props.partyIndex;
    this._threshold = props.threshold;
    this._totalParties = props.totalParties;
    this._encryptedShareData = props.encryptedShareData;
    this._encryptionKeyId = props.encryptionKeyId;
    this._status = props.status;
    this._accessCount = props.accessCount;
    this._lastAccessedAt = props.lastAccessedAt;
    this._createdAt = props.createdAt;
    this._updatedAt = props.updatedAt;
    this._revokedAt = props.revokedAt;
  }

  static create(params: {
    userId: bigint;
    accountSequence: string;
    publicKey: string;
    encryptedShareData: string;
    encryptionKeyId: string;
    threshold?: number;
    totalParties?: number;
  }): BackupShare {
    const now = new Date();
    return new BackupShare({
      shareId: null,
      userId: params.userId,
      accountSequence: params.accountSequence,
      publicKey: params.publicKey,
      partyIndex: 2, // Backup = Party 2
      threshold: params.threshold ?? 2,
      totalParties: params.totalParties ?? 3,
      encryptedShareData: params.encryptedShareData,
      encryptionKeyId: params.encryptionKeyId,
      status: BackupShareStatus.ACTIVE,
      accessCount: 0,
      lastAccessedAt: null,
      createdAt: now,
      updatedAt: now,
      revokedAt: null,
    });
  }

  static reconstitute(props: BackupShareProps): BackupShare {
    return new BackupShare(props);
  }

  recordAccess(): void {
    if (this._status !== BackupShareStatus.ACTIVE) {
      throw new DomainError('Cannot access revoked or rotated share');
    }
    this._accessCount++;
    this._lastAccessedAt = new Date();
    this._updatedAt = new Date();
  }

  revoke(reason: string): void {
    if (this._status === BackupShareStatus.REVOKED) {
      throw new DomainError('Share already revoked');
    }
    this._status = BackupShareStatus.REVOKED;
    this._revokedAt = new Date();
    this._updatedAt = new Date();
  }

  rotate(newEncryptedData: string, newKeyId: string): void {
    if (this._status === BackupShareStatus.REVOKED) {
      throw new DomainError('Cannot rotate revoked share');
    }
    this._encryptedShareData = newEncryptedData;
    this._encryptionKeyId = newKeyId;
    this._status = BackupShareStatus.ACTIVE;
    this._updatedAt = new Date();
  }

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

  // Getters
  get shareId(): bigint | null {
    return this._shareId;
  }
  get userId(): bigint {
    return this._userId;
  }
  get accountSequence(): string {
    return this._accountSequence;
  }
  get publicKey(): string {
    return this._publicKey;
  }
  get partyIndex(): number {
    return this._partyIndex;
  }
  get threshold(): number {
    return this._threshold;
  }
  get totalParties(): number {
    return this._totalParties;
  }
  get encryptedShareData(): string {
    return this._encryptedShareData;
  }
  get encryptionKeyId(): string {
    return this._encryptionKeyId;
  }
  get status(): BackupShareStatus {
    return this._status;
  }
  get accessCount(): number {
    return this._accessCount;
  }
  get lastAccessedAt(): Date | null {
    return this._lastAccessedAt;
  }
  get createdAt(): Date {
    return this._createdAt;
  }
  get updatedAt(): Date {
    return this._updatedAt;
  }
  get revokedAt(): Date | null {
    return this._revokedAt;
  }

  // For persistence
  setShareId(id: bigint): void {
    if (this._shareId !== null) {
      throw new DomainError('Share ID already set');
    }
    this._shareId = id;
  }

  toProps(): BackupShareProps {
    return {
      shareId: this._shareId,
      userId: this._userId,
      accountSequence: this._accountSequence,
      publicKey: this._publicKey,
      partyIndex: this._partyIndex,
      threshold: this._threshold,
      totalParties: this._totalParties,
      encryptedShareData: this._encryptedShareData,
      encryptionKeyId: this._encryptionKeyId,
      status: this._status,
      accessCount: this._accessCount,
      lastAccessedAt: this._lastAccessedAt,
      createdAt: this._createdAt,
      updatedAt: this._updatedAt,
      revokedAt: this._revokedAt,
    };
  }
}
