import { SequelizeRepository } from "@/contexts/common/infrastructure/sequelize/SequelizeRepository";

import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
import { ICollection, IQueryCriteria, UniqueID } from "@shared/contexts";
import { Transaction } from "sequelize";
import { IInvoiceRepository, Invoice } from "../domain";
import { IInvoiceMapper } from "./mappers";

export type QueryParams = {
  pagination: Record<string, any>;
  filters: Record<string, any>;
};

export class InvoiceRepository
  extends SequelizeRepository<Invoice>
  implements IInvoiceRepository
{
  protected mapper: IInvoiceMapper;

  public constructor(props: {
    mapper: IInvoiceMapper;
    adapter: ISequelizeAdapter;
    transaction: Transaction;
  }) {
    const { adapter, mapper, transaction } = props;
    super({ adapter, transaction });
    this.mapper = mapper;
  }

  public async getById(id: UniqueID): Promise<Invoice | null> {
    const rawContact: any = await this._getById("Invoice_Model", id, {
      include: [
        { association: "items" },
        {
          association: "participants",
          include: [
            { association: "shippingAddress" },
            { association: "billingAddress" },
          ],
        },
      ],
    });

    if (!rawContact === true) {
      return null;
    }

    return this.mapper.mapToDomain(rawContact);
  }

  public async findAll(
    queryCriteria?: IQueryCriteria
  ): Promise<ICollection<Invoice>> {
    const { rows, count } = await this._findAll(
      "Invoice_Model",
      queryCriteria,
      {
        include: [
          {
            association: "participants",
            separate: true,
          },
        ],
      }
    );

    return this.mapper.mapArrayAndCountToDomain(rows, count);
  }

  public async save(invoice: Invoice): Promise<void> {
    const { items, participants, ...invoiceData } =
      this.mapper.mapToPersistence(invoice);

    await this.adapter
      .getModel("Invoice_Model")
      .create(invoiceData, { transaction: this.transaction });

    await this.adapter
      .getModel("InvoiceItem_Model")
      .bulkCreate(items, { transaction: this.transaction });

    await this.adapter
      .getModel("InvoiceParticipant_Model")
      .bulkCreate(participants, { transaction: this.transaction });

    await this.adapter
      .getModel("InvoiceParticipantAddress_Model")
      .bulkCreate(
        [participants[0].billingAddress, participants[0].shippingAddress],
        { transaction: this.transaction }
      );
  }

  public removeById(id: UniqueID): Promise<void> {
    return this._removeById("Invoice_Model", id);
  }

  public async exists(id: UniqueID): Promise<boolean> {
    return this._exists("Invoice_Model", "id", id.toString());
  }
}
