import {
  IUseCase,
  IUseCaseError,
  IUseCaseRequest,
  UseCaseError,
} from "@/contexts/common/application";
import { IRepositoryManager } from "@/contexts/common/domain";
import { IInfrastructureError } from "@/contexts/common/infrastructure";
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
import { Result, UniqueID } from "@shared/contexts";
import fs from "fs/promises";
import { IProfileRepository, Profile } from "../domain";
import { ProfileLogotype } from "../domain/entities/ProfileLogotype";
import { IProfileContext } from "../infrastructure";

export interface IUpdateProfileLogoUseCaseRequest extends IUseCaseRequest {
  userId: UniqueID;
  file: Express.Multer.File;
}

export type UploadProfileLogoResponseOrError =
  | Result<never, IUseCaseError> // Misc errors (value objects)
  | Result<Profile, never>; // Success!

export class UploadProfileLogoUseCase
  implements IUseCase<IUpdateProfileLogoUseCaseRequest, Promise<UploadProfileLogoResponseOrError>>
{
  private _adapter: ISequelizeAdapter;
  private _repositoryManager: IRepositoryManager;
  private _defaults: Record<string, any>;

  constructor(props: IProfileContext) {
    this._adapter = props.adapter;
    this._repositoryManager = props.repositoryManager;
    this._defaults = props.defaults;
  }

  async execute(
    request: IUpdateProfileLogoUseCaseRequest
  ): Promise<UploadProfileLogoResponseOrError> {
    const { userId, file } = request;

    // Comprobar que existe el profile
    const exitsOrError = await this._getProfileDealer(userId);
    if (exitsOrError.isFailure) {
      const message = `Profile not found`;
      return Result.fail(
        UseCaseError.create(UseCaseError.NOT_FOUND_ERROR, message, {
          path: "userId",
        })
      );
    }

    const profile = exitsOrError.object;

    // Borro del logo anterior
    this._deleteOldLogo(profile.logo);

    // Actualizar el perfil con datos actualizados
    profile.logo = ProfileLogotype.create(file.filename).object;

    // Guardar los cambios
    return this._saveProfile(profile);
  }

  private async _deleteOldLogo(logo: ProfileLogotype) {
    const logo_path = logo.relativePath();
    await fs.rm(logo_path);
  }

  private async _saveProfile(updatedProfile: Profile) {
    // Guardar el contacto
    const transaction = this._adapter.startTransaction();
    const profileRepository = this._getProfileRepository();

    try {
      await transaction.complete(async (t) => {
        const profileRepo = profileRepository({ transaction: t });
        await profileRepo.update(updatedProfile);
      });

      return Result.ok<Profile>(updatedProfile);
    } catch (error: unknown) {
      const _error = error as IInfrastructureError;
      return Result.fail(UseCaseError.create(UseCaseError.REPOSITORY_ERROR, _error.message));
    }
  }

  private async _getProfileDealer(userId: UniqueID) {
    const transaction = this._adapter.startTransaction();
    const profileRepoBuilder = this._getProfileRepository();

    let profile: Profile | null = null;

    try {
      await transaction.complete(async (t) => {
        const profileRepo = profileRepoBuilder({ transaction: t });
        profile = await profileRepo.getByUserId(userId);
      });

      if (!profile) {
        return Result.fail(UseCaseError.create(UseCaseError.NOT_FOUND_ERROR, "Profile not found"));
      }

      return Result.ok<Profile>(profile!);
    } catch (error: unknown) {
      const _error = error as IInfrastructureError;
      return Result.fail(
        UseCaseError.create(UseCaseError.REPOSITORY_ERROR, "Error al consultar el usuario", _error)
      );
    }
  }

  private _getProfileRepository() {
    return this._repositoryManager.getRepository<IProfileRepository>("Profile");
  }
}
