/**
 * Application Use Cases: Notification Preferences
 * 
 * Manages user notification preferences.
 */

import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { ChannelPreference, TypePreference } from '../../domain/entities/NotificationPreference';
import { NotificationPreference } from '../../domain/entities/NotificationPreference';
import { NotificationPreferenceRepository } from '../../domain/repositories/NotificationPreferenceRepository';
import { NotificationChannel, NotificationType } from '../../domain/types/NotificationTypes';

/**
 * Query: GetNotificationPreferencesQuery
 */
export interface GetNotificationPreferencesInput {
  driverId: string;
}

export interface GetNotificationPreferencesResult {
  preference: NotificationPreference;
}

export type GetNotificationPreferencesErrorCode = 'REPOSITORY_ERROR';

export class GetNotificationPreferencesQuery {
  constructor(
    private readonly preferenceRepository: NotificationPreferenceRepository,
    private readonly logger: Logger,
  ) {}

  async execute(
    input: GetNotificationPreferencesInput,
  ): Promise<Result<GetNotificationPreferencesResult, ApplicationErrorCode<GetNotificationPreferencesErrorCode, { message: string }>>> {
    const { driverId } = input;
    this.logger.debug(`Fetching notification preferences for driver: ${driverId}`);
    try {
      const preferences = await this.preferenceRepository.getOrCreateDefault(driverId);
      this.logger.info(`Successfully fetched preferences for driver: ${driverId}`);
      return Result.ok<GetNotificationPreferencesResult, ApplicationErrorCode<GetNotificationPreferencesErrorCode, { message: string }>>({ preference: preferences });
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error));
      this.logger.error(`Failed to fetch preferences for driver: ${driverId}`, err);
      return Result.err<GetNotificationPreferencesResult, ApplicationErrorCode<'REPOSITORY_ERROR', { message: string }>>({
        code: 'REPOSITORY_ERROR',
        details: { message: err.message },
      });
    }
  }
}

/**
 * Use Case: UpdateChannelPreferenceUseCase
 */
export interface UpdateChannelPreferenceCommand {
  driverId: string;
  channel: NotificationChannel;
  preference: ChannelPreference;
}

export interface UpdateChannelPreferenceResult {
  driverId: string;
  channel: NotificationChannel;
}

export type UpdateChannelPreferenceErrorCode =
  | 'REPOSITORY_ERROR';

export class UpdateChannelPreferenceUseCase {
  constructor(
    private readonly preferenceRepository: NotificationPreferenceRepository,
    private readonly logger: Logger,
  ) {}

  async execute(
    command: UpdateChannelPreferenceCommand,
  ): Promise<Result<UpdateChannelPreferenceResult, ApplicationErrorCode<UpdateChannelPreferenceErrorCode, { message: string }>>> {
    this.logger.debug(
      `Updating channel preference for driver: ${command.driverId}, channel: ${command.channel}, preference: ${JSON.stringify(command.preference)}`,
    );
    try {
      const preferences = await this.preferenceRepository.getOrCreateDefault(
        command.driverId,
      );
      const updated = preferences.updateChannel(command.channel, command.preference);
      await this.preferenceRepository.save(updated);
      this.logger.info(
        `Successfully updated channel preference for driver: ${command.driverId}`,
      );
      return Result.ok<UpdateChannelPreferenceResult, ApplicationErrorCode<UpdateChannelPreferenceErrorCode, { message: string }>>({
        driverId: command.driverId,
        channel: command.channel,
      });
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error));
      this.logger.error(
        `Failed to update channel preference for driver: ${command.driverId}, channel: ${command.channel}`,
        err,
      );
      return Result.err<UpdateChannelPreferenceResult, ApplicationErrorCode<UpdateChannelPreferenceErrorCode, { message: string }>>({
        code: 'REPOSITORY_ERROR',
        details: { message: err.message },
      });
    }
  }
}

/**
 * Use Case: UpdateTypePreferenceUseCase
 */
export interface UpdateTypePreferenceCommand {
  driverId: string;
  type: NotificationType;
  preference: TypePreference;
}

export interface UpdateTypePreferenceResult {
  driverId: string;
  type: NotificationType;
}

export type UpdateTypePreferenceErrorCode = 'REPOSITORY_ERROR';

export class UpdateTypePreferenceUseCase {
  constructor(
    private readonly preferenceRepository: NotificationPreferenceRepository,
    private readonly logger: Logger,
  ) {}

  async execute(
    command: UpdateTypePreferenceCommand,
  ): Promise<Result<UpdateTypePreferenceResult, ApplicationErrorCode<UpdateTypePreferenceErrorCode, { message: string }>>> {
    this.logger.debug(
      `Updating type preference for driver: ${command.driverId}, type: ${command.type}, preference: ${JSON.stringify(command.preference)}`,
    );
    try {
      const preferences = await this.preferenceRepository.getOrCreateDefault(
        command.driverId,
      );
      const updated = preferences.updateTypePreference(command.type, command.preference);
      await this.preferenceRepository.save(updated);
      this.logger.info(
        `Successfully updated type preference for driver: ${command.driverId}`,
      );
      return Result.ok<UpdateTypePreferenceResult, ApplicationErrorCode<UpdateTypePreferenceErrorCode, { message: string }>>({
        driverId: command.driverId,
        type: command.type,
      });
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error));
      this.logger.error(
        `Failed to update type preference for driver: ${command.driverId}, type: ${command.type}`,
        err,
      );
      return Result.err<UpdateTypePreferenceResult, ApplicationErrorCode<GetNotificationPreferencesErrorCode, { message: string }>>({
        code: 'REPOSITORY_ERROR',
        details: { message: err.message },
      });
    }
  }
}

/**
 * Use Case: UpdateQuietHoursUseCase
 */
export interface UpdateQuietHoursCommand {
  driverId: string;
  startHour: number | undefined;
  endHour: number | undefined;
}

export interface UpdateQuietHoursResult {
  driverId: string;
  startHour: number | undefined;
  endHour: number | undefined;
}

export type UpdateQuietHoursErrorCode =
  | 'INVALID_START_HOUR'
  | 'INVALID_END_HOUR'
  | 'REPOSITORY_ERROR';

export class UpdateQuietHoursUseCase {
  constructor(
    private readonly preferenceRepository: NotificationPreferenceRepository,
    private readonly logger: Logger,
  ) {}

  async execute(
    command: UpdateQuietHoursCommand,
  ): Promise<Result<UpdateQuietHoursResult, ApplicationErrorCode<UpdateQuietHoursErrorCode, { message: string }>>> {
    this.logger.debug(
      `Updating quiet hours for driver: ${command.driverId}, startHour: ${command.startHour}, endHour: ${command.endHour}`,
    );
    try {
      // Validate hours if provided
      if (command.startHour !== undefined && (command.startHour < 0 || command.startHour > 23)) {
        this.logger.warn(
          `Invalid start hour provided for driver: ${command.driverId}. startHour: ${command.startHour}`,
        );
        return Result.err<UpdateQuietHoursResult, ApplicationErrorCode<UpdateQuietHoursErrorCode, { message: string }>>({
          code: 'INVALID_START_HOUR',
          details: { message: 'Start hour must be between 0 and 23' },
        });
      }
      if (command.endHour !== undefined && (command.endHour < 0 || command.endHour > 23)) {
        this.logger.warn(
          `Invalid end hour provided for driver: ${command.driverId}. endHour: ${command.endHour}`,
        );
        return Result.err<UpdateQuietHoursResult, ApplicationErrorCode<UpdateQuietHoursErrorCode, { message: string }>>({
          code: 'INVALID_END_HOUR',
          details: { message: 'End hour must be between 0 and 23' },
        });
      }

      const preferences = await this.preferenceRepository.getOrCreateDefault(
        command.driverId,
      );
      const updated = preferences.updateQuietHours(
        command.startHour,
        command.endHour,
      );
      await this.preferenceRepository.save(updated);
      this.logger.info(`Successfully updated quiet hours for driver: ${command.driverId}`);
      return Result.ok<UpdateQuietHoursResult, ApplicationErrorCode<UpdateQuietHoursErrorCode, { message: string }>>({
        driverId: command.driverId,
        startHour: command.startHour,
        endHour: command.endHour,
      });
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error));
      this.logger.error(`Failed to update quiet hours for driver: ${command.driverId}`, err);
      return Result.err<UpdateQuietHoursResult, ApplicationErrorCode<UpdateTypePreferenceErrorCode, { message: string }>>({
        code: 'REPOSITORY_ERROR',
        details: { message: err.message },
      });
    }
  }
}

/**
 * Use Case: SetDigestModeUseCase
 */
export interface SetDigestModeCommand {
  driverId: string;
  enabled: boolean;
  frequencyHours?: number;
}

export interface SetDigestModeResult {
  driverId: string;
  enabled: boolean;
  frequencyHours?: number | undefined;
}

export type SetDigestModeErrorCode =
  | 'INVALID_FREQUENCY'
  | 'REPOSITORY_ERROR';

export class SetDigestModeUseCase {
  constructor(
    private readonly preferenceRepository: NotificationPreferenceRepository,
  ) {}

  async execute(
    command: SetDigestModeCommand,
  ): Promise<Result<SetDigestModeResult, ApplicationErrorCode<SetDigestModeErrorCode, { message: string }>>> {
    if (command.frequencyHours !== undefined && command.frequencyHours < 1) {
      return Result.err<SetDigestModeResult, ApplicationErrorCode<SetDigestModeErrorCode, { message: string }>>({
        code: 'INVALID_FREQUENCY',
        details: { message: 'Digest frequency must be at least 1 hour' },
      });
    }

    try {
      const preferences = await this.preferenceRepository.getOrCreateDefault(
        command.driverId,
      );
      const updated = preferences.setDigestMode(
        command.enabled,
        command.frequencyHours,
      );
      await this.preferenceRepository.save(updated);
      const result: SetDigestModeResult = {
        driverId: command.driverId,
        enabled: command.enabled,
        frequencyHours: command.frequencyHours,
      };
      return Result.ok<SetDigestModeResult, ApplicationErrorCode<SetDigestModeErrorCode, { message: string }>>(result);
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error));
      return Result.err<SetDigestModeResult, ApplicationErrorCode<SetDigestModeErrorCode, { message: string }>>({
        code: 'REPOSITORY_ERROR',
        details: { message: err.message },
      });
    }
  }
}
