import {
  CurrencyData,
  Language,
  Note,
  Percentage,
  TextValueObject,
  UTCDateValue,
  UniqueID,
} from "@shared/contexts";

import { ISequelizeMapper, SequelizeMapper } from "@/contexts/common/infrastructure";
import {
  IQuoteProps,
  Quote,
  QuoteCustomer,
  QuoteCustomerReference,
  QuoteReference,
} from "../../domain";
import { QuoteStatus } from "../../domain/entities/Quotes/QuoteStatus";
import { ISalesContext } from "../Sales.context";
import { QuoteCreationAttributes, Quote_Model } from "../sequelize";
import { IQuoteItemMapper, createQuoteItemMapper } from "./quoteItem.mapper";

export interface IQuoteMapper
  extends ISequelizeMapper<Quote_Model, QuoteCreationAttributes, Quote> {}

export const createQuoteMapper = (context: ISalesContext): IQuoteMapper =>
  new QuoteMapper({
    context,
    quoteItemMapper: createQuoteItemMapper(context),
  });

class QuoteMapper
  extends SequelizeMapper<Quote_Model, QuoteCreationAttributes, Quote>
  implements IQuoteMapper
{
  public constructor(props: { quoteItemMapper: IQuoteItemMapper; context: ISalesContext }) {
    super(props);
  }

  protected toDomainMappingImpl(source: Quote_Model): Quote {
    const id = this.mapsValue(source, "id", UniqueID.create);

    const items = (this.props.quoteItemMapper as IQuoteItemMapper).mapArrayToDomain(source.items, {
      sourceParent: source,
    });

    const props: IQuoteProps = {
      status: this.mapsValue(source, "status", QuoteStatus.create),
      date: this.mapsValue(source, "date", UTCDateValue.create),
      reference: this.mapsValue(source, "reference", QuoteReference.create),
      currency: this.mapsValue(source, "currency_code", CurrencyData.createFromCode),
      language: this.mapsValue(source, "lang_code", Language.createFromCode),
      customerReference: this.mapsValue(
        source,
        "customer_reference",
        QuoteCustomerReference.create
      ),
      customer: this.mapsValue(source, "customer_information", QuoteCustomer.create),

      validity: this.mapsValue(source, "validity", Note.create),
      paymentMethod: this.mapsValue(source, "payment_method", Note.create),
      notes: this.mapsValue(source, "notes", TextValueObject.create),

      items,

      /*subtotal: this.mapsValue(source, "subtotal_price", (subtotal_price) =>
        MoneyValue.create({
          amount: subtotal_price,
          currencyCode: source.currency_code,
          precision: 2,
        })
      ),*/

      discount: this.mapsValue(source, "discount", (discount) =>
        Percentage.create({
          amount: discount,
          scale: 2,
        })
      ),

      tax: this.mapsValue(source, "tax", (tax) =>
        Percentage.create({
          amount: tax,
          scale: 2,
        })
      ),

      /*totalPrice: this.mapsValue(source, "total_price", (total_price) =>
        MoneyValue.create({
          amount: total_price,
          currencyCode: source.currency_code,
          precision: 2,
        })
      ),*/

      dealerId: this.mapsValue(source, "dealer_id", UniqueID.create),

      dateSent: this.mapsValue(source, "date_sent", UTCDateValue.create),
    };

    const quoteOrError = Quote.create(props, id);

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

    return quoteOrError.object;
  }

  protected toPersistenceMappingImpl(source: Quote) {
    const items = (this.props.quoteItemMapper as IQuoteItemMapper).mapCollectionToPersistence(
      source.items,
      { sourceParent: source }
    );

    const quote: QuoteCreationAttributes = {
      id: source.id.toPrimitive(),
      status: source.status.toPrimitive(),
      date: source.date.toPrimitive(),
      reference: source.reference.toPrimitive(),
      currency_code: source.currency.toPrimitive(),
      lang_code: source.language.toPrimitive(),
      customer_reference: source.customerReference.toPrimitive(),
      customer_information: source.customer.toPrimitive(),
      validity: source.validity.toPrimitive(),
      payment_method: source.paymentMethod.toPrimitive(),
      notes: source.notes.toPrimitive(),

      subtotal_price: source.subtotalPrice.convertScale(2).toPrimitive(),

      discount: source.discount.convertScale(2).toPrimitive(),
      discount_price: source.discountPrice.convertScale(2).toPrimitive(),

      before_tax_price: source.beforeTaxPrice.convertScale(2).toPrimitive(),

      tax: source.tax.convertScale(2).toPrimitive(),
      tax_price: source.taxPrice.convertScale(2).toPrimitive(),

      total_price: source.totalPrice.convertScale(2).toPrimitive(),

      items,
      dealer_id: source.dealerId.toPrimitive(),

      date_sent: source.dateSent.toPrimitive() === "" ? null : source.dateSent.toPrimitive(),
    };

    return quote;
  }
}
