/**
 * Domain Entity: NotificationPreference
 * 
 * Represents a user's notification preferences for different channels and types.
 */

import type { IEntity } from '@core/shared/domain';
import type { NotificationType, NotificationChannel } from '../types/NotificationTypes';
import { NotificationDomainError } from '../errors/NotificationDomainError';
import { QuietHours } from '../value-objects/QuietHours';

export interface ChannelPreference {
  /** Whether this channel is enabled */
  enabled: boolean;
  /** Channel-specific settings (e.g., discord webhook URL, email address) */
  settings?: Record<string, string>;
}

export interface TypePreference {
  /** Whether notifications of this type are enabled */
  enabled: boolean;
  /** Which channels to use for this type (overrides global) */
  channels?: NotificationChannel[];
}

export interface NotificationPreferenceProps {
  /** Aggregate ID for this preference (usually same as driverId) */
  id: string;
  /** Driver ID this preference belongs to */
  driverId: string;
  /** Global channel preferences */
  channels: Record<NotificationChannel, ChannelPreference>;
  /** Per-type preferences (optional overrides) */
  typePreferences?: Partial<Record<NotificationType, TypePreference>>;
  /** Whether to receive digest emails instead of individual notifications */
  digestMode?: boolean;
  /** Digest frequency in hours (e.g., 24 for daily) */
  digestFrequencyHours?: number;
  /** Quiet hours start (0-23) */
  quietHoursStart?: number;
  /** Quiet hours end (0-23) */
  quietHoursEnd?: number;
  /** Last updated timestamp */
  updatedAt: Date;
}

export class NotificationPreference implements IEntity<string> {
  private constructor(private readonly props: NotificationPreferenceProps) {}

  static create(
    props: Omit<NotificationPreferenceProps, 'updatedAt'> & { updatedAt?: Date },
  ): NotificationPreference {
    if (!props.id) throw new NotificationDomainError('Preference ID is required');
    if (!props.driverId) throw new NotificationDomainError('Driver ID is required');
    if (!props.channels) throw new NotificationDomainError('Channel preferences are required');

    return new NotificationPreference({
      ...props,
      updatedAt: props.updatedAt ?? new Date(),
    });
  }

  /**
   * Create default preferences for a new user
   */
  static createDefault(driverId: string): NotificationPreference {
    return new NotificationPreference({
      id: driverId,
      driverId,
      channels: {
        in_app: { enabled: true },
        email: { enabled: false },
        discord: { enabled: false },
        push: { enabled: false },
      },
      digestMode: false,
      updatedAt: new Date(),
    });
  }

  get id(): string { return this.props.id; }
  get driverId(): string { return this.props.driverId; }
  get channels(): Record<NotificationChannel, ChannelPreference> { return { ...this.props.channels }; }
  get typePreferences(): Partial<Record<NotificationType, TypePreference>> | undefined {
    return this.props.typePreferences ? { ...this.props.typePreferences } : undefined;
  }
  get digestMode(): boolean { return this.props.digestMode ?? false; }
  get digestFrequencyHours(): number { return this.props.digestFrequencyHours ?? 24; }
  get quietHoursStart(): number | undefined { return this.props.quietHoursStart; }
  get quietHoursEnd(): number | undefined { return this.props.quietHoursEnd; }
  get updatedAt(): Date { return this.props.updatedAt; }

  get quietHours(): QuietHours | undefined {
    if (this.props.quietHoursStart === undefined || this.props.quietHoursEnd === undefined) {
      return undefined;
    }
    return QuietHours.create(this.props.quietHoursStart, this.props.quietHoursEnd);
  }

  /**
   * Check if a specific channel is enabled
   */
  isChannelEnabled(channel: NotificationChannel): boolean {
    return this.props.channels[channel]?.enabled ?? false;
  }

  /**
   * Check if a specific notification type is enabled
   */
  isTypeEnabled(type: NotificationType): boolean {
    const typePref = this.props.typePreferences?.[type];
    return typePref?.enabled ?? true; // Default to enabled if not specified
  }

  /**
   * Get enabled channels for a specific notification type
   */
  getEnabledChannelsForType(type: NotificationType): NotificationChannel[] {
    // Check type-specific channel overrides
    const typePref = this.props.typePreferences?.[type];
    if (typePref?.channels && typePref.channels.length > 0) {
      return typePref.channels.filter(ch => this.isChannelEnabled(ch));
    }

    // Fall back to globally enabled channels
    return (Object.keys(this.props.channels) as NotificationChannel[])
      .filter(ch => this.isChannelEnabled(ch));
  }

  /**
   * Check if current time is in quiet hours
   */
  isInQuietHours(): boolean {
    const quietHours = this.quietHours;
    if (!quietHours) {
      return false;
    }

    const now = new Date();
    return quietHours.containsHour(now.getHours());
  }

  /**
   * Update channel preference
   */
  updateChannel(channel: NotificationChannel, preference: ChannelPreference): NotificationPreference {
    return new NotificationPreference({
      ...this.props,
      channels: {
        ...this.props.channels,
        [channel]: preference,
      },
      updatedAt: new Date(),
    });
  }

  /**
   * Update type preference
   */
  updateTypePreference(type: NotificationType, preference: TypePreference): NotificationPreference {
    return new NotificationPreference({
      ...this.props,
      typePreferences: {
        ...this.props.typePreferences,
        [type]: preference,
      },
      updatedAt: new Date(),
    });
  }

  /**
   * Update quiet hours
   */
  updateQuietHours(start: number | undefined, end: number | undefined): NotificationPreference {
    const props = this.toJSON();

    if (start === undefined || end === undefined) {
      delete props.quietHoursStart;
      delete props.quietHoursEnd;
    } else {
      const validated = QuietHours.create(start, end);
      props.quietHoursStart = validated.props.startHour;
      props.quietHoursEnd = validated.props.endHour;
    }

    props.updatedAt = new Date();
    return NotificationPreference.create(props);
  }

  /**
   * Toggle digest mode
   */
  setDigestMode(enabled: boolean, frequencyHours?: number): NotificationPreference {
    const props = this.toJSON();
    props.digestMode = enabled;
    if (frequencyHours !== undefined) {
      props.digestFrequencyHours = frequencyHours;
    }
    props.updatedAt = new Date();
    return NotificationPreference.create(props);
  }

  /**
   * Convert to plain object for serialization
   */
  toJSON(): NotificationPreferenceProps {
    return { ...this.props };
  }
}