import { Provider } from '@nestjs/common';

import { CookieIdentitySessionAdapter } from '@adapters/identity/session/CookieIdentitySessionAdapter';
import { LoginUseCase } from '@core/identity/application/use-cases/LoginUseCase';
import { LogoutUseCase } from '@core/identity/application/use-cases/LogoutUseCase';
import { SignupUseCase } from '@core/identity/application/use-cases/SignupUseCase';
import { ForgotPasswordUseCase } from '@core/identity/application/use-cases/ForgotPasswordUseCase';
import { ResetPasswordUseCase } from '@core/identity/application/use-cases/ResetPasswordUseCase';
import { DemoLoginUseCase } from '../../development/use-cases/DemoLoginUseCase';
import type { IdentitySessionPort } from '@core/identity/application/ports/IdentitySessionPort';
import type { IAuthRepository } from '@core/identity/domain/repositories/IAuthRepository';
import type { IMagicLinkRepository } from '@core/identity/domain/repositories/IMagicLinkRepository';
import type { IPasswordHashingService } from '@core/identity/domain/services/PasswordHashingService';
import type { IMagicLinkNotificationPort } from '@core/identity/domain/ports/IMagicLinkNotificationPort';
import type { LoginResult } from '@core/identity/application/use-cases/LoginUseCase';
import type { LogoutResult } from '@core/identity/application/use-cases/LogoutUseCase';
import type { SignupResult } from '@core/identity/application/use-cases/SignupUseCase';
import type { ForgotPasswordResult } from '@core/identity/application/use-cases/ForgotPasswordUseCase';
import type { ResetPasswordResult } from '@core/identity/application/use-cases/ResetPasswordUseCase';
import type { DemoLoginResult } from '../../development/use-cases/DemoLoginUseCase';
import type { Logger, UseCaseOutputPort } from '@core/shared/application';

import {
  AUTH_REPOSITORY_TOKEN,
  PASSWORD_HASHING_SERVICE_TOKEN,
  USER_REPOSITORY_TOKEN,
  MAGIC_LINK_REPOSITORY_TOKEN,
} from '../../persistence/identity/IdentityPersistenceTokens';

import { AuthSessionPresenter } from './presenters/AuthSessionPresenter';
import { CommandResultPresenter } from './presenters/CommandResultPresenter';
import { ForgotPasswordPresenter } from './presenters/ForgotPasswordPresenter';
import { ResetPasswordPresenter } from './presenters/ResetPasswordPresenter';
import { DemoLoginPresenter } from './presenters/DemoLoginPresenter';
import { ConsoleMagicLinkNotificationAdapter } from '@adapters/notifications/ports/ConsoleMagicLinkNotificationAdapter';

// Define the tokens for dependency injection
export { AUTH_REPOSITORY_TOKEN, USER_REPOSITORY_TOKEN, PASSWORD_HASHING_SERVICE_TOKEN };
export const LOGGER_TOKEN = 'Logger';
export const IDENTITY_SESSION_PORT_TOKEN = 'IdentitySessionPort';
export const LOGIN_USE_CASE_TOKEN = 'LoginUseCase';
export const SIGNUP_USE_CASE_TOKEN = 'SignupUseCase';
export const LOGOUT_USE_CASE_TOKEN = 'LogoutUseCase';
export const FORGOT_PASSWORD_USE_CASE_TOKEN = 'ForgotPasswordUseCase';
export const RESET_PASSWORD_USE_CASE_TOKEN = 'ResetPasswordUseCase';
export const DEMO_LOGIN_USE_CASE_TOKEN = 'DemoLoginUseCase';

export const AUTH_SESSION_OUTPUT_PORT_TOKEN = 'AuthSessionOutputPort';
export const COMMAND_RESULT_OUTPUT_PORT_TOKEN = 'CommandResultOutputPort';
export const FORGOT_PASSWORD_OUTPUT_PORT_TOKEN = 'ForgotPasswordOutputPort';
export const RESET_PASSWORD_OUTPUT_PORT_TOKEN = 'ResetPasswordOutputPort';
export const DEMO_LOGIN_OUTPUT_PORT_TOKEN = 'DemoLoginOutputPort';
export const MAGIC_LINK_NOTIFICATION_PORT_TOKEN = 'MagicLinkNotificationPort';

export const AuthProviders: Provider[] = [
  {
    provide: IDENTITY_SESSION_PORT_TOKEN,
    useFactory: (logger: Logger) => new CookieIdentitySessionAdapter(logger),
    inject: [LOGGER_TOKEN],
  },
  {
    provide: AuthSessionPresenter,
    useClass: AuthSessionPresenter,
  },
  {
    provide: CommandResultPresenter,
    useClass: CommandResultPresenter,
  },
  {
    provide: AUTH_SESSION_OUTPUT_PORT_TOKEN,
    useExisting: AuthSessionPresenter,
  },
  {
    provide: COMMAND_RESULT_OUTPUT_PORT_TOKEN,
    useExisting: CommandResultPresenter,
  },
  {
    provide: LOGIN_USE_CASE_TOKEN,
    useFactory: (
      authRepo: IAuthRepository,
      passwordHashing: IPasswordHashingService,
      logger: Logger,
      output: UseCaseOutputPort<LoginResult>,
    ) => new LoginUseCase(authRepo, passwordHashing, logger, output),
    inject: [AUTH_REPOSITORY_TOKEN, PASSWORD_HASHING_SERVICE_TOKEN, LOGGER_TOKEN, AUTH_SESSION_OUTPUT_PORT_TOKEN],
  },
  {
    provide: SIGNUP_USE_CASE_TOKEN,
    useFactory: (
      authRepo: IAuthRepository,
      passwordHashing: IPasswordHashingService,
      logger: Logger,
      output: UseCaseOutputPort<SignupResult>,
    ) => new SignupUseCase(authRepo, passwordHashing, logger, output),
    inject: [AUTH_REPOSITORY_TOKEN, PASSWORD_HASHING_SERVICE_TOKEN, LOGGER_TOKEN, AUTH_SESSION_OUTPUT_PORT_TOKEN],
  },
  {
    provide: LOGOUT_USE_CASE_TOKEN,
    useFactory: (sessionPort: IdentitySessionPort, logger: Logger, output: UseCaseOutputPort<LogoutResult>) =>
      new LogoutUseCase(sessionPort, logger, output),
    inject: [IDENTITY_SESSION_PORT_TOKEN, LOGGER_TOKEN, COMMAND_RESULT_OUTPUT_PORT_TOKEN],
  },
  {
    provide: ForgotPasswordPresenter,
    useClass: ForgotPasswordPresenter,
  },
  {
    provide: ResetPasswordPresenter,
    useClass: ResetPasswordPresenter,
  },
  {
    provide: DemoLoginPresenter,
    useClass: DemoLoginPresenter,
  },
  {
    provide: FORGOT_PASSWORD_OUTPUT_PORT_TOKEN,
    useExisting: ForgotPasswordPresenter,
  },
  {
    provide: RESET_PASSWORD_OUTPUT_PORT_TOKEN,
    useExisting: ResetPasswordPresenter,
  },
  {
    provide: DEMO_LOGIN_OUTPUT_PORT_TOKEN,
    useExisting: DemoLoginPresenter,
  },
  {
    provide: MAGIC_LINK_NOTIFICATION_PORT_TOKEN,
    useFactory: (logger: Logger) => new ConsoleMagicLinkNotificationAdapter(logger),
    inject: [LOGGER_TOKEN],
  },
  {
    provide: FORGOT_PASSWORD_USE_CASE_TOKEN,
    useFactory: (
      authRepo: IAuthRepository,
      magicLinkRepo: IMagicLinkRepository,
      notificationPort: IMagicLinkNotificationPort,
      logger: Logger,
      output: UseCaseOutputPort<ForgotPasswordResult>,
    ) => new ForgotPasswordUseCase(authRepo, magicLinkRepo, notificationPort, logger, output),
    inject: [AUTH_REPOSITORY_TOKEN, MAGIC_LINK_REPOSITORY_TOKEN, MAGIC_LINK_NOTIFICATION_PORT_TOKEN, LOGGER_TOKEN, FORGOT_PASSWORD_OUTPUT_PORT_TOKEN],
  },
  {
    provide: RESET_PASSWORD_USE_CASE_TOKEN,
    useFactory: (
      authRepo: IAuthRepository,
      magicLinkRepo: IMagicLinkRepository,
      passwordHashing: IPasswordHashingService,
      logger: Logger,
      output: UseCaseOutputPort<ResetPasswordResult>,
    ) => new ResetPasswordUseCase(authRepo, magicLinkRepo, passwordHashing, logger, output),
    inject: [AUTH_REPOSITORY_TOKEN, MAGIC_LINK_REPOSITORY_TOKEN, PASSWORD_HASHING_SERVICE_TOKEN, LOGGER_TOKEN, RESET_PASSWORD_OUTPUT_PORT_TOKEN],
  },
  {
    provide: DEMO_LOGIN_USE_CASE_TOKEN,
    useFactory: (
      authRepo: IAuthRepository,
      passwordHashing: IPasswordHashingService,
      logger: Logger,
      output: UseCaseOutputPort<DemoLoginResult>,
    ) => new DemoLoginUseCase(authRepo, passwordHashing, logger, output),
    inject: [AUTH_REPOSITORY_TOKEN, PASSWORD_HASHING_SERVICE_TOKEN, LOGGER_TOKEN, DEMO_LOGIN_OUTPUT_PORT_TOKEN],
  },
];
