import { DuplicateEntityError, IPresenterRegistry, ITransactionManager } from "@erp/core/api";
import { UniqueID } from "@repo/rdx-ddd";
import { Result } from "@repo/rdx-utils";
import { Transaction } from "sequelize";
import { CreateCustomerRequestDTO } from "../../../../common";
import { logger } from "../../..//helpers";
import { CustomerApplicationService } from "../../../domain";
import { CustomerFullPresenter } from "../../presenters";
import { CustomerNotExistsInCompanySpecification } from "../../specs";
import { mapDTOToCreateCustomerProps } from "./map-dto-to-create-customer-props";

type CreateCustomerUseCaseInput = {
  companyId: UniqueID;
  dto: CreateCustomerRequestDTO;
};

export class CreateCustomerUseCase {
  constructor(
    private readonly service: CustomerApplicationService,
    private readonly transactionManager: ITransactionManager,
    private readonly presenterRegistry: IPresenterRegistry
  ) {}

  public execute(params: CreateCustomerUseCaseInput) {
    const { dto, companyId } = params;
    const presenter = this.presenterRegistry.getPresenter({
      resource: "customer",
      projection: "FULL",
    }) as CustomerFullPresenter;

    // 1) Mapear DTO → props de dominio
    const dtoResult = mapDTOToCreateCustomerProps(dto);
    if (dtoResult.isFailure) {
      return Result.fail(dtoResult.error);
    }

    const { props, id } = dtoResult.data;

    // 2) Construir entidad de dominio
    const buildResult = this.service.buildCustomerInCompany(companyId, props, id);
    if (buildResult.isFailure) {
      return Result.fail(buildResult.error);
    }

    const newCustomer = buildResult.data;

    // 3) Ejecutar bajo transacción: verificar duplicado → persistir → ensamblar vista
    return this.transactionManager.complete(async (transaction: Transaction) => {
      try {
        // Verificar que no exista ya un cliente con el mismo id en la companyId
        const spec = new CustomerNotExistsInCompanySpecification(
          this.service,
          companyId,
          transaction
        );

        const isNew = await spec.isSatisfiedBy(newCustomer.id);
        logger.debug(`isNew => ${isNew}`, { label: "CreateCustomerUseCase.execute" });

        if (!isNew) {
          return Result.fail(new DuplicateEntityError("Customer", "id", String(newCustomer.id)));
        }

        logger.debug(JSON.stringify(newCustomer, null, 6));

        const saveResult = await this.service.saveCustomer(newCustomer, transaction);
        if (saveResult.isFailure) {
          return Result.fail(saveResult.error);
        }

        const customer = saveResult.data;
        const dto = presenter.toOutput(customer);

        return Result.ok(dto);
      } catch (error: unknown) {
        return Result.fail(error as Error);
      }
    });
  }
}
