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 {
  IUpdateProfile_Request_DTO,
  Percentage,
  Result,
  TextValueObject,
  UniqueID,
} from "@shared/contexts";
import { IProfileRepository, Profile } from "../domain";

export interface IUpdateProfileUseCaseRequest extends IUseCaseRequest {
  userId: UniqueID;
  profileDTO: IUpdateProfile_Request_DTO;
}

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

export class UpdateProfileUseCase
  implements IUseCase<IUpdateProfileUseCaseRequest, Promise<UpdateProfileResponseOrError>>
{
  private _adapter: ISequelizeAdapter;
  private _repositoryManager: IRepositoryManager;

  constructor(props: { adapter: ISequelizeAdapter; repositoryManager: IRepositoryManager }) {
    this._adapter = props.adapter;
    this._repositoryManager = props.repositoryManager;
  }

  async execute(request: IUpdateProfileUseCaseRequest): Promise<UpdateProfileResponseOrError> {
    const { userId, profileDTO } = 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;

    // Actualizar el perfil con datos actualizados
    profile.contactInformation = TextValueObject.create(profileDTO.contact_information).object;
    profile.defaultPaymentMethod = TextValueObject.create(profileDTO.default_payment_method).object;
    profile.defaultLegalTerms = TextValueObject.create(profileDTO.default_legal_terms).object;
    profile.defaultNotes = TextValueObject.create(profileDTO.default_notes).object;
    profile.defaultQuoteValidity = TextValueObject.create(profileDTO.default_quote_validity).object;

    const taxOrError = Percentage.create(profileDTO.default_tax);
    if (taxOrError.isFailure) {
      return Result.fail(taxOrError.error);
    }

    profile.defaultTax = taxOrError.object;

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

  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 dealerRepoBuilder = this._getProfileRepository();

    let profile: Profile | null = null;

    try {
      await transaction.complete(async (t) => {
        const dealerRepo = dealerRepoBuilder({ transaction: t });
        profile = await dealerRepo.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");
  }
}
