import { Invoice } from "@contexts/invoices/domain";
import { IInvoiceRepository } from "@contexts/invoices/domain/repositories/invoice-repository.interface";
import { UniqueID } from "core/common/domain";
import { Collection, Result } from "core/common/helpers";
import { SequelizeRepository } from "core/common/infrastructure";
import { Transaction } from "sequelize";
import { IInvoiceMapper, invoiceMapper } from "../mappers/invoice.mapper";
import { InvoiceItemModel } from "./invoice-item.model";
import { InvoiceModel } from "./invoice.model";

class InvoiceRepository extends SequelizeRepository<Invoice> implements IInvoiceRepository {
  private readonly _mapper!: IInvoiceMapper;

  /**
   * 🔹 Función personalizada para mapear errores de unicidad en autenticación
   */
  private _customErrorMapper(error: Error): string | null {
    if (error.name === "SequelizeUniqueConstraintError") {
      return "Invoice with this email already exists";
    }

    return null;
  }

  constructor(mapper: IInvoiceMapper) {
    super();
    this._mapper = mapper;
  }

  async invoiceExists(id: UniqueID, transaction?: Transaction): Promise<Result<boolean, Error>> {
    try {
      const _invoice = await this._getById(InvoiceModel, id, {}, transaction);

      return Result.ok(Boolean(id.equals(_invoice.id)));
    } catch (error: any) {
      return this._handleDatabaseError(error, this._customErrorMapper);
    }
  }

  async findAll(transaction?: Transaction): Promise<Result<Collection<Invoice>, Error>> {
    try {
      const rawInvoices: any = await this._findAll(
        InvoiceModel,
        {
          include: [
            {
              model: InvoiceItemModel,
              as: "items",
            },
          ],
        },
        transaction
      );

      if (!rawInvoices === true) {
        return Result.fail(new Error("Invoice with email not exists"));
      }

      return this._mapper.mapArrayToDomain(rawInvoices);
    } catch (error: any) {
      return this._handleDatabaseError(error, this._customErrorMapper);
    }
  }

  async getById(id: UniqueID, transaction?: Transaction): Promise<Result<Invoice, Error>> {
    try {
      const rawInvoice: any = await this._getById(
        InvoiceModel,
        id,
        {
          include: [
            {
              model: InvoiceItemModel,
              as: "items",
            },
          ],
        },
        transaction
      );

      if (!rawInvoice === true) {
        return Result.fail(new Error(`Invoice with id ${id.toString()} not exists`));
      }

      return this._mapper.mapToDomain(rawInvoice);
    } catch (error: any) {
      return this._handleDatabaseError(error, this._customErrorMapper);
    }
  }

  async deleteById(id: UniqueID, transaction?: Transaction): Promise<Result<boolean, Error>> {
    try {
      this._deleteById(InvoiceModel, id);
      return Result.ok<boolean>(true);
    } catch (error: any) {
      return this._handleDatabaseError(error, this._customErrorMapper);
    }
  }

  async create(invoice: Invoice, transaction?: Transaction): Promise<void> {
    const invoiceData = this._mapper.mapToPersistence(invoice);
    await this._save(InvoiceModel, invoice.id, invoiceData, {}, transaction);
  }

  async update(invoice: Invoice, transaction?: Transaction): Promise<void> {
    const invoiceData = this._mapper.mapToPersistence(invoice);
    await this._save(InvoiceModel, invoice.id, invoiceData, {}, transaction);
  }
}

const invoiceRepository = new InvoiceRepository(invoiceMapper);
export { invoiceRepository };
