/**
 * Domain Entity: SponsorshipRequest
 *
 * Represents a sponsorship application from a Sponsor to any sponsorable entity
 * (driver, team, race, or league/season). The entity owner must approve/reject.
 */
 
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';
import type { IEntity } from '@core/shared/domain';

import type { Money } from '../value-objects/Money';
import type { SponsorshipTier } from './season/SeasonSponsorship';

export type SponsorableEntityType = 'driver' | 'team' | 'race' | 'season';
export type SponsorshipRequestStatus = 'pending' | 'accepted' | 'rejected' | 'withdrawn';

export interface SponsorshipRequestProps {
  id: string;
  sponsorId: string;
  entityType: SponsorableEntityType;
  entityId: string;
  tier: SponsorshipTier;
  offeredAmount: Money;
  message?: string;
  status: SponsorshipRequestStatus;
  createdAt: Date;
  respondedAt?: Date;
  respondedBy?: string; // driverId of the person who accepted/rejected
  rejectionReason?: string;
}

export class SponsorshipRequest implements IEntity<string> {
  readonly id: string;
  readonly sponsorId: string;
  readonly entityType: SponsorableEntityType;
  readonly entityId: string;
  readonly tier: SponsorshipTier;
  readonly offeredAmount: Money;
  readonly message: string | undefined;
  readonly status: SponsorshipRequestStatus;
  readonly createdAt: Date;
  readonly respondedAt: Date | undefined;
  readonly respondedBy: string | undefined;
  readonly rejectionReason: string | undefined;

  private constructor(props: SponsorshipRequestProps) {
    this.id = props.id;
    this.sponsorId = props.sponsorId;
    this.entityType = props.entityType;
    this.entityId = props.entityId;
    this.tier = props.tier;
    this.offeredAmount = props.offeredAmount;
    this.message = props.message;
    this.status = props.status;
    this.createdAt = props.createdAt;
    this.respondedAt = props.respondedAt;
    this.respondedBy = props.respondedBy;
    this.rejectionReason = props.rejectionReason;
  }

  static create(props: Omit<SponsorshipRequestProps, 'createdAt' | 'status'> & {
    createdAt?: Date;
    status?: SponsorshipRequestStatus;
  }): SponsorshipRequest {
    this.validate(props);

    return new SponsorshipRequest({
      ...props,
      createdAt: props.createdAt ?? new Date(),
      status: props.status ?? 'pending',
    });
  }

  static rehydrate(props: SponsorshipRequestProps): SponsorshipRequest {
    return new SponsorshipRequest(props);
  }

  private static validate(props: Omit<SponsorshipRequestProps, 'createdAt' | 'status'>): void {
    if (!props.id || props.id.trim().length === 0) {
      throw new RacingDomainValidationError('SponsorshipRequest ID is required');
    }

    if (!props.sponsorId || props.sponsorId.trim().length === 0) {
      throw new RacingDomainValidationError('SponsorshipRequest sponsorId is required');
    }

    if (!props.entityType) {
      throw new RacingDomainValidationError('SponsorshipRequest entityType is required');
    }

    if (!props.entityId || props.entityId.trim().length === 0) {
      throw new RacingDomainValidationError('SponsorshipRequest entityId is required');
    }

    if (!props.tier) {
      throw new RacingDomainValidationError('SponsorshipRequest tier is required');
    }

    if (!props.offeredAmount) {
      throw new RacingDomainValidationError('SponsorshipRequest offeredAmount is required');
    }

    if (props.offeredAmount.amount <= 0) {
      throw new RacingDomainValidationError('SponsorshipRequest offeredAmount must be greater than zero');
    }
  }

  /**
   * Accept the sponsorship request
   */
  accept(respondedBy: string): SponsorshipRequest {
    if (this.status !== 'pending') {
      throw new RacingDomainInvariantError(`Cannot accept a ${this.status} sponsorship request`);
    }

    if (!respondedBy || respondedBy.trim().length === 0) {
      throw new RacingDomainValidationError('respondedBy is required when accepting');
    }

    const base: SponsorshipRequestProps = {
      id: this.id,
      sponsorId: this.sponsorId,
      entityType: this.entityType,
      entityId: this.entityId,
      tier: this.tier,
      offeredAmount: this.offeredAmount,
      status: 'accepted',
      createdAt: this.createdAt,
      respondedAt: new Date(),
      respondedBy,
    };

    const withMessage =
      this.message !== undefined ? { ...base, message: this.message } : base;

    const next: SponsorshipRequestProps =
      this.rejectionReason !== undefined
        ? { ...withMessage, rejectionReason: this.rejectionReason }
        : withMessage;

    return new SponsorshipRequest(next);
  }

  /**
   * Reject the sponsorship request
   */
  reject(respondedBy: string, reason?: string): SponsorshipRequest {
    if (this.status !== 'pending') {
      throw new RacingDomainInvariantError(`Cannot reject a ${this.status} sponsorship request`);
    }

    if (!respondedBy || respondedBy.trim().length === 0) {
      throw new RacingDomainValidationError('respondedBy is required when rejecting');
    }

    const base: SponsorshipRequestProps = {
      id: this.id,
      sponsorId: this.sponsorId,
      entityType: this.entityType,
      entityId: this.entityId,
      tier: this.tier,
      offeredAmount: this.offeredAmount,
      status: 'rejected',
      createdAt: this.createdAt,
      respondedAt: new Date(),
      respondedBy,
    };

    const withMessage =
      this.message !== undefined ? { ...base, message: this.message } : base;

    const next: SponsorshipRequestProps =
      reason !== undefined ? { ...withMessage, rejectionReason: reason } : withMessage;

    return new SponsorshipRequest(next);
  }

  /**
   * Withdraw the sponsorship request (by the sponsor)
   */
  withdraw(): SponsorshipRequest {
    if (this.status !== 'pending') {
      throw new RacingDomainInvariantError(`Cannot withdraw a ${this.status} sponsorship request`);
    }

    const base: SponsorshipRequestProps = {
      id: this.id,
      sponsorId: this.sponsorId,
      entityType: this.entityType,
      entityId: this.entityId,
      tier: this.tier,
      offeredAmount: this.offeredAmount,
      status: 'withdrawn',
      createdAt: this.createdAt,
      respondedAt: new Date(),
    };

    const withRespondedBy =
      this.respondedBy !== undefined
        ? { ...base, respondedBy: this.respondedBy }
        : base;

    const withMessage =
      this.message !== undefined
        ? { ...withRespondedBy, message: this.message }
        : withRespondedBy;

    const next: SponsorshipRequestProps =
      this.rejectionReason !== undefined
        ? { ...withMessage, rejectionReason: this.rejectionReason }
        : withMessage;

    return new SponsorshipRequest(next);
  }

  /**
   * Check if request is pending
   */
  isPending(): boolean {
    return this.status === 'pending';
  }

  /**
   * Check if request was accepted
   */
  isAccepted(): boolean {
    return this.status === 'accepted';
  }

  /**
   * Get platform fee for this request
   */
  getPlatformFee(): Money {
    return this.offeredAmount.calculatePlatformFee();
  }

  /**
   * Get net amount after platform fee
   */
  getNetAmount(): Money {
    return this.offeredAmount.calculateNetAmount();
  }
}