/**
 * PartyShare Specification
 *
 * Contains business rules and validation specifications for PartyShare aggregate.
 * Used to encapsulate complex domain logic and validation rules.
 */

import { PartyShare } from '../../entities/party-share.entity';
import { PartyShareStatus, PartyShareType } from '../../enums';
import { PartyId, PublicKey } from '../../value-objects';

/**
 * Specification interface for PartyShare
 */
export interface PartyShareSpecification {
  isSatisfiedBy(share: PartyShare): boolean;
  and(other: PartyShareSpecification): PartyShareSpecification;
  or(other: PartyShareSpecification): PartyShareSpecification;
  not(): PartyShareSpecification;
}

/**
 * Base specification class with composition methods
 */
abstract class BaseSpecification implements PartyShareSpecification {
  abstract isSatisfiedBy(share: PartyShare): boolean;

  and(other: PartyShareSpecification): PartyShareSpecification {
    return new AndSpecification(this, other);
  }

  or(other: PartyShareSpecification): PartyShareSpecification {
    return new OrSpecification(this, other);
  }

  not(): PartyShareSpecification {
    return new NotSpecification(this);
  }
}

/**
 * Composite AND specification
 */
class AndSpecification extends BaseSpecification {
  constructor(
    private readonly left: PartyShareSpecification,
    private readonly right: PartyShareSpecification,
  ) {
    super();
  }

  isSatisfiedBy(share: PartyShare): boolean {
    return this.left.isSatisfiedBy(share) && this.right.isSatisfiedBy(share);
  }
}

/**
 * Composite OR specification
 */
class OrSpecification extends BaseSpecification {
  constructor(
    private readonly left: PartyShareSpecification,
    private readonly right: PartyShareSpecification,
  ) {
    super();
  }

  isSatisfiedBy(share: PartyShare): boolean {
    return this.left.isSatisfiedBy(share) || this.right.isSatisfiedBy(share);
  }
}

/**
 * NOT specification
 */
class NotSpecification extends BaseSpecification {
  constructor(private readonly spec: PartyShareSpecification) {
    super();
  }

  isSatisfiedBy(share: PartyShare): boolean {
    return !this.spec.isSatisfiedBy(share);
  }
}

// ============================================================================
// Concrete Specifications
// ============================================================================

/**
 * Specification: Share is active
 */
export class IsActiveSpecification extends BaseSpecification {
  isSatisfiedBy(share: PartyShare): boolean {
    return share.status === PartyShareStatus.ACTIVE;
  }
}

/**
 * Specification: Share belongs to a specific party
 */
export class BelongsToPartySpecification extends BaseSpecification {
  constructor(private readonly partyId: PartyId) {
    super();
  }

  isSatisfiedBy(share: PartyShare): boolean {
    return share.belongsToParty(this.partyId);
  }
}

/**
 * Specification: Share has specific public key
 */
export class HasPublicKeySpecification extends BaseSpecification {
  constructor(private readonly publicKey: PublicKey) {
    super();
  }

  isSatisfiedBy(share: PartyShare): boolean {
    return share.hasPublicKey(this.publicKey);
  }
}

/**
 * Specification: Share is of a specific type
 */
export class IsShareTypeSpecification extends BaseSpecification {
  constructor(private readonly shareType: PartyShareType) {
    super();
  }

  isSatisfiedBy(share: PartyShare): boolean {
    return share.shareType === this.shareType;
  }
}

/**
 * Specification: Share meets minimum threshold requirement
 */
export class MeetsThresholdSpecification extends BaseSpecification {
  constructor(private readonly participantCount: number) {
    super();
  }

  isSatisfiedBy(share: PartyShare): boolean {
    return share.validateThreshold(this.participantCount);
  }
}

/**
 * Specification: Share was used recently (within given hours)
 */
export class WasUsedRecentlySpecification extends BaseSpecification {
  constructor(private readonly withinHours: number = 24) {
    super();
  }

  isSatisfiedBy(share: PartyShare): boolean {
    if (!share.lastUsedAt) {
      return false;
    }
    const cutoffTime = new Date(Date.now() - this.withinHours * 60 * 60 * 1000);
    return share.lastUsedAt >= cutoffTime;
  }
}

/**
 * Specification: Share can be used for signing
 */
export class CanBeUsedForSigningSpecification extends BaseSpecification {
  constructor(private readonly participantCount: number) {
    super();
  }

  isSatisfiedBy(share: PartyShare): boolean {
    const isActive = new IsActiveSpecification();
    const meetsThreshold = new MeetsThresholdSpecification(this.participantCount);
    return isActive.and(meetsThreshold).isSatisfiedBy(share);
  }
}

/**
 * Specification: Share is eligible for rotation
 */
export class IsEligibleForRotationSpecification extends BaseSpecification {
  constructor(private readonly minAgeInDays: number = 30) {
    super();
  }

  isSatisfiedBy(share: PartyShare): boolean {
    const isActive = new IsActiveSpecification();
    if (!isActive.isSatisfiedBy(share)) {
      return false;
    }

    // Check if share is old enough to be rotated
    const cutoffTime = new Date(Date.now() - this.minAgeInDays * 24 * 60 * 60 * 1000);
    return share.createdAt <= cutoffTime;
  }
}

// ============================================================================
// Specification Factory
// ============================================================================

export const PartyShareSpecs = {
  isActive: () => new IsActiveSpecification(),
  belongsToParty: (partyId: PartyId) => new BelongsToPartySpecification(partyId),
  hasPublicKey: (publicKey: PublicKey) => new HasPublicKeySpecification(publicKey),
  isShareType: (type: PartyShareType) => new IsShareTypeSpecification(type),
  meetsThreshold: (count: number) => new MeetsThresholdSpecification(count),
  wasUsedRecently: (hours?: number) => new WasUsedRecentlySpecification(hours),
  canBeUsedForSigning: (count: number) => new CanBeUsedForSigningSpecification(count),
  isEligibleForRotation: (days?: number) => new IsEligibleForRotationSpecification(days),
};
