import { ISequelizeMapper, SequelizeMapper } from "@/contexts/common/infrastructure";
import { Name, TINNumber, UniqueID } from "@shared/contexts";

import { Contact, IContactProps } from "../../domain";
import { IInvoicingContext } from "../InvoicingContext";
import { Contact_Model, TCreationContact_Model } from "../sequelize/contact.mo.del";
import { IContactAddressMapper, createContactAddressMapper } from "./contactAddress.mapper";

export interface IContactMapper
  extends ISequelizeMapper<Contact_Model, TCreationContact_Model, Contact> {}

class ContactMapper
  extends SequelizeMapper<Contact_Model, TCreationContact_Model, Contact>
  implements IContactMapper
{
  public constructor(props: { addressMapper: IContactAddressMapper; context: IInvoicingContext }) {
    super(props);
  }

  protected toDomainMappingImpl(source: Contact_Model, params: any): Contact {
    if (!source.billingAddress) {
      this.handleRequiredFieldError(
        "billingAddress",
        new Error("Missing participant's billing address")
      );
    }

    if (!source.shippingAddress) {
      this.handleRequiredFieldError(
        "shippingAddress",
        new Error("Missing participant's shipping address")
      );
    }

    const billingAddress = this.props.addressMapper.mapToDomain(source.billingAddress!, params);

    const shippingAddress = this.props.addressMapper.mapToDomain(source.shippingAddress!, params);

    const props: IContactProps = {
      tin: this.mapsValue(source, "tin", TINNumber.create),
      firstName: this.mapsValue(source, "first_name", Name.create),
      lastName: this.mapsValue(source, "last_name", Name.create),
      companyName: this.mapsValue(source, "company_name", Name.create),
      billingAddress,
      shippingAddress,
    };

    const id = this.mapsValue(source, "id", UniqueID.create);
    const contactOrError = Contact.create(props, id);

    if (contactOrError.isFailure) {
      throw contactOrError.error;
    }

    return contactOrError.object;
  }
}

export const createContactMapper = (context: IInvoicingContext): IContactMapper =>
  new ContactMapper({
    addressMapper: createContactAddressMapper(context),
    context,
  });
