import { ISequelizeMapper, SequelizeMapper } from "@/contexts/common/infrastructure";
import { Name, TINNumber, UniqueID } from "@shared/contexts";
import {
  IInvoiceCustomerProps,
  Invoice,
  InvoiceCustomer,
  InvoiceParticipantBillingAddress,
  InvoiceParticipantShippingAddress,
} from "../../domain";
import { IInvoicingContext } from "../InvoicingContext";
import { InvoiceParticipant_Model, TCreationInvoiceParticipant_Model } from "../sequelize";
import {
  IInvoiceParticipantAddressMapper,
  createInvoiceParticipantAddressMapper,
} from "./invoiceParticipantAddress.mapper";

export interface IInvoiceParticipantMapper
  extends ISequelizeMapper<
    InvoiceParticipant_Model,
    TCreationInvoiceParticipant_Model,
    InvoiceCustomer
  > {}

export const createInvoiceParticipantMapper = (
  context: IInvoicingContext
): IInvoiceParticipantMapper =>
  new InvoiceParticipantMapper({
    context,
    addressMapper: createInvoiceParticipantAddressMapper(context),
  });

class InvoiceParticipantMapper
  extends SequelizeMapper<
    InvoiceParticipant_Model,
    TCreationInvoiceParticipant_Model,
    InvoiceCustomer
  >
  implements IInvoiceParticipantMapper
{
  public constructor(props: {
    addressMapper: IInvoiceParticipantAddressMapper;
    context: IInvoicingContext;
  }) {
    super(props);
  }

  protected toDomainMappingImpl(source: InvoiceParticipant_Model, params: any) {
    /*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 = source.billingAddress
      ? ((this.props.addressMapper as IInvoiceParticipantAddressMapper).mapToDomain(
          source.billingAddress,
          params
        ) as InvoiceParticipantBillingAddress)
      : undefined;

    const shippingAddress = source.shippingAddress
      ? ((this.props.addressMapper as IInvoiceParticipantAddressMapper).mapToDomain(
          source.shippingAddress,
          params
        ) as InvoiceParticipantShippingAddress)
      : undefined;

    const props: IInvoiceCustomerProps = {
      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, "participant_id", UniqueID.create);
    const participantOrError = InvoiceCustomer.create(props, id);

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

    return participantOrError.object;
  }

  protected toPersistenceMappingImpl(
    source: InvoiceCustomer,
    params: { sourceParent: Invoice }
  ): TCreationInvoiceParticipant_Model {
    const { sourceParent } = params;

    return {
      invoice_id: sourceParent.id.toPrimitive(),

      participant_id: source.id.toPrimitive(),
      tin: source.tin.toPrimitive(),
      first_name: source.firstName.toPrimitive(),
      last_name: source.lastName.toPrimitive(),
      company_name: source.companyName.toPrimitive(),

      billingAddress: (
        this.props.addressMapper as IInvoiceParticipantAddressMapper
      ).mapToPersistence(source.billingAddress!, { sourceParent: source }),

      shippingAddress: (
        this.props.addressMapper as IInvoiceParticipantAddressMapper
      ).mapToPersistence(source.shippingAddress!, { sourceParent: source }),
    };
  }
}
