import { ISequelizeAdapter, SequelizeRepository } from "@/contexts/common/infrastructure/sequelize";
import { ICollection, IQueryCriteria, UniqueID, UTCDateValue } from "@shared/contexts";
import { ModelDefined, Transaction } from "sequelize";

import { IQuoteRepository } from "../domain";
import { Quote, QuoteReference, QuoteStatus } from "../domain/entities";
import { ISalesContext } from "./Sales.context";
import { createQuoteMapper, IQuoteMapper } from "./mappers/quote.mapper";

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

export class QuoteRepository extends SequelizeRepository<Quote> implements IQuoteRepository {
  protected mapper: IQuoteMapper;

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

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

  public async existsByReference(reference: QuoteReference): Promise<boolean> {
    return this._exists("Quote_Model", "reference", reference.toPrimitive());
  }

  public async create(quote: Quote): Promise<void> {
    const quoteData = this.mapper.mapToPersistence(quote);
    await this._save("Quote_Model", quote.id, quoteData);
  }

  public async update(quote: Quote): Promise<void> {
    console.time("update");
    const quoteData = this.mapper.mapToPersistence(quote);

    const QuoteItem_Model: ModelDefined<any, any> = this._adapter.getModel("QuoteItem_Model");

    await Promise.all([
      this._save("Quote_Model", quote.id, quoteData, {}),
      QuoteItem_Model.destroy({
        where: {
          quote_id: quoteData.id,
        },
        transaction: this._transaction,
        force: true,
      }),
    ]);

    await QuoteItem_Model.bulkCreate(quoteData.items, {
      transaction: this._transaction,
    });

    console.timeEnd("update");
  }

  public async getById(id: UniqueID): Promise<Quote | null> {
    const QuoteItem_Model: ModelDefined<any, any> = this._adapter.getModel("QuoteItem_Model");

    const rawQuote: any = await this._getById("Quote_Model", id, {
      include: [
        {
          model: QuoteItem_Model,
          as: "items",
        },
      ],
      order: [
        [
          {
            model: QuoteItem_Model,
            as: "items",
          },
          "position",
          "asc",
        ],
      ],
    });

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

    return this.mapper.mapToDomain(rawQuote);
  }

  public async findAll(queryCriteria?: IQueryCriteria): Promise<ICollection<Quote>> {
    const QuoteItem_Model: ModelDefined<any, any> = this._adapter.getModel("QuoteItem_Model");

    const { rows, count } = await this._findAll("Quote_Model", queryCriteria, {
      include: [], // esto es para quitar las asociaciones al hacer la consulta
      /*include: [
        {
          model: QuoteItem_Model,
          as: "items",
        },
      ],*/
      order: [
        ["date", "DESC"],
        ["customer_information", "ASC"],
        ["status", "ASC"],
      ],
    });

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

  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  public async removeById(id: UniqueID, force: boolean = false): Promise<void> {
    return this._removeById("Quote_Model", id);
  }

  public async findLastQuoteByDealerId(dealerId: UniqueID): Promise<Quote | null> {
    const Quote_Model: ModelDefined<any, any> = this._adapter.getModel("Quote_Model");
    const QuoteItem_Model: ModelDefined<any, any> = this._adapter.getModel("QuoteItem_Model");

    const rawQuote: any = await Quote_Model.findOne({
      include: [
        {
          model: QuoteItem_Model,
          as: "items",
        },
      ],
      where: { dealer_id: dealerId.toString() },
      order: [["created_at", "DESC"]], // Ordenamos por referencia en orden descendente
    });

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

    return this.mapper.mapToDomain(rawQuote);
  }

  public async findLastReferenceByDealerId(dealerId: UniqueID): Promise<QuoteReference | null> {
    const quote = await this.findLastQuoteByDealerId(dealerId);
    return quote ? quote.reference : null;
  }

  public async updateStatusById(quoteId: UniqueID, newStatus: QuoteStatus): Promise<void> {
    const quoteStatusData = newStatus.toPrimitive(); //this.mapper.mapToPersistence(quote);
    const _model = this._adapter.getModel("Quote_Model");

    await _model.update(
      {
        status: quoteStatusData,
      },
      {
        where: { id: quoteId.toPrimitive() },
        transaction: this._transaction,
      }
    );
  }

  public async updateSentDateById(quoteId: UniqueID, sentDate: UTCDateValue): Promise<void> {
    const quoteSentData = sentDate.toPrimitive(); //this.mapper.mapToPersistence(quote);
    const _model = this._adapter.getModel("Quote_Model");

    await _model.update(
      {
        date_sent: quoteSentData,
      },
      {
        where: { id: quoteId.toPrimitive() },
        transaction: this._transaction,
      }
    );
  }
}

export const registerQuoteRepository = (context: ISalesContext) => {
  const adapter = context.adapter;
  const repoManager = context.repositoryManager;

  repoManager.registerRepository("Quote", (params = { transaction: null }) => {
    const { transaction } = params;

    return new QuoteRepository({
      transaction,
      adapter,
      mapper: createQuoteMapper(context),
    });
  });
};
