import { Account, AccountStatus } from "@/contexts/accounts/domain/";
import {
  EmailAddress,
  PhoneNumber,
  PostalAddress,
  TINNumber,
  UniqueID,
} from "@/core/common/domain";
import {
  type ISequelizeMapper,
  type MapperParamsType,
  SequelizeMapper,
} from "@/core/common/infrastructure/sequelize/sequelize-mapper";
import { Maybe, Result } from "@repo/rdx-utils";
import { AccountCreationAttributes, AccountModel } from "../sequelize/account.model";

export interface IAccountMapper
  extends ISequelizeMapper<AccountModel, AccountCreationAttributes, Account> {}

export class AccountMapper
  extends SequelizeMapper<AccountModel, AccountCreationAttributes, Account>
  implements IAccountMapper
{
  public mapToDomain(source: AccountModel, params?: MapperParamsType): Result<Account, Error> {
    const idOrError = UniqueID.create(source.id);
    const statusOrError = AccountStatus.create(source.status);
    const tinOrError = TINNumber.create(source.tin);
    const emailOrError = EmailAddress.create(source.email);
    const phoneOrError = PhoneNumber.create(source.phone);
    const faxOrError = PhoneNumber.createNullable(source.fax);
    const postalAddressOrError = PostalAddress.create({
      street: source.street,
      city: source.city,
      state: source.state,
      postalCode: source.postal_code,
      country: source.country,
    });

    const result = Result.combine([
      idOrError,
      statusOrError,
      tinOrError,
      emailOrError,
      phoneOrError,
      faxOrError,
      postalAddressOrError,
    ]);

    if (result.isFailure) {
      return Result.fail(result.error);
    }

    return Account.create(
      {
        status: statusOrError.data,
        isFreelancer: source.is_companyr,
        name: source.name,
        tradeName: source.trade_name ? Maybe.some(source.trade_name) : Maybe.none(),
        tin: tinOrError.data,
        address: postalAddressOrError.data,
        email: emailOrError.data,
        phone: phoneOrError.data,
        fax: faxOrError.data,
        website: source.website ? Maybe.some(source.website) : Maybe.none(),
        legalRecord: source.legal_record,
        defaultTax: source.default_tax,
        langCode: source.language_code,
        currencyCode: source.currency_code,
        logo: source.logo ? Maybe.some(source.logo) : Maybe.none(),
      },
      idOrError.data
    );
  }

  public mapToPersistence(source: Account, params?: MapperParamsType): AccountCreationAttributes {
    return {
      id: source.id.toPrimitive(),
      is_companyr: source.isFreelancer,
      name: source.name,
      trade_name: source.tradeName.getOrUndefined(),
      tin: source.tin.toPrimitive(),

      street: source.address.street,
      city: source.address.city,
      state: source.address.state,
      postal_code: source.address.postalCode,
      country: source.address.country,

      email: source.email.toPrimitive(),
      phone: source.phone.toPrimitive(),
      fax: source.fax.isSome() ? source.fax.getOrUndefined()?.toPrimitive() : undefined,
      website: source.website.getOrUndefined(),

      legal_record: source.legalRecord,
      default_tax: source.defaultTax,
      status: source.isActive ? "active" : "inactive",
      language_code: source.langCode,
      currency_code: source.currencyCode,
      logo: source.logo.getOrUndefined(),
    };
  }
}

const accountMapper: AccountMapper = new AccountMapper();
export { accountMapper };
