import {
  EmailAddress,
  PhoneNumber,
  PostalAddress,
  TINNumber,
  UniqueID,
} from "@/core/common/domain";

import { Account, IAccountProps, IAccountService } from "@/contexts/accounts/domain";
import { ITransactionManager } from "@/core/common/infrastructure/database";
import { logger } from "@/core/logger";
import { Maybe, Result } from "@repo/rdx-utils";
import { IUpdateAccountRequestDTO } from "../presentation";

export class UpdateAccountUseCase {
  constructor(
    private readonly accountService: IAccountService,
    private readonly transactionManager: ITransactionManager
  ) {}

  public execute(
    accountID: UniqueID,
    dto: Partial<IUpdateAccountRequestDTO>
  ): Promise<Result<Account, Error>> {
    return this.transactionManager.complete(async (transaction) => {
      try {
        const validOrErrors = this.validateAccountData(dto);
        if (validOrErrors.isFailure) {
          return Result.fail(validOrErrors.error);
        }

        const data = validOrErrors.data;

        // Update account with dto
        return await this.accountService.updateAccountById(accountID, data, transaction);
      } catch (error: unknown) {
        logger.error(error as Error);
        return Result.fail(error as Error);
      }
    });
  }

  private validateAccountData(
    dto: Partial<IUpdateAccountRequestDTO>
  ): Result<Partial<IAccountProps>, Error> {
    const errors: Error[] = [];
    const validatedData: Partial<IAccountProps> = {};

    if (dto.is_companyr) {
      validatedData.isFreelancer = dto.is_companyr;
    }

    if (dto.name) {
      validatedData.name = dto.name;
    }

    if (dto.trade_name) {
      validatedData.tradeName = Maybe.some(dto.trade_name);
    }

    if (dto.tin) {
      const tinOrError = TINNumber.create(dto.tin);
      if (tinOrError.isFailure) errors.push(tinOrError.error);
      else validatedData.tin = tinOrError.data;
    }

    if (dto.email) {
      const emailOrError = EmailAddress.create(dto.email);
      if (emailOrError.isFailure) errors.push(emailOrError.error);
      else validatedData.email = emailOrError.data;
    }

    if (dto.phone) {
      const phoneOrError = PhoneNumber.create(dto.phone);
      if (phoneOrError.isFailure) errors.push(phoneOrError.error);
      else validatedData.phone = phoneOrError.data;
    }

    if (dto.fax) {
      const faxOrError = PhoneNumber.create(dto.fax);
      if (faxOrError.isFailure) errors.push(faxOrError.error);
      else validatedData.fax = Maybe.some(faxOrError.data);
    }

    if (dto.street || dto.city || dto.state || dto.postal_code || dto.country) {
      const postalAddressOrError = PostalAddress.create({
        street: dto.street ?? "",
        city: dto.city ?? "",
        state: dto.state ?? "",
        postalCode: dto.postal_code ?? "",
        country: dto.country ?? "",
      });
      if (postalAddressOrError.isFailure) errors.push(postalAddressOrError.error);
      else validatedData.address = postalAddressOrError.data;
    }

    if (dto.website) {
      validatedData.website = Maybe.some(dto.website);
    }

    if (dto.legal_record) {
      validatedData.legalRecord = dto.legal_record;
    }

    if (dto.default_tax) {
      validatedData.defaultTax = dto.default_tax;
    }

    if (dto.language_code) {
      validatedData.langCode = dto.language_code;
    }

    if (dto.currency_code) {
      validatedData.currencyCode = dto.currency_code;
    }

    if (dto.logo) {
      validatedData.logo = Maybe.some(dto.logo);
    }

    if (errors.length > 0) {
      const message = errors.map((err) => err.message).toString();
      return Result.fail(new Error(message));
    }
    return Result.ok(validatedData);
  }
}
