import { EntityNotFoundError, SequelizeRepository, translateSequelizeError } from "@erp/core/api";
import { Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server";
import { UniqueID } from "@repo/rdx-ddd";
import { Collection, Result } from "@repo/rdx-utils";
import { Transaction } from "sequelize";
import { IVerifactuRecordRepository, VerifactuRecord } from "../../domain";
import {
  //  VerifactuRecordListDTO,
  IVerifactuRecordDomainMapper,
} from "../mappers";
import { VerifactuRecordModel } from "./models/verifactu-record.model";

export class VerifactuRecordRepository
  extends SequelizeRepository<VerifactuRecord>
  implements IVerifactuRecordRepository
{
  getById(id: UniqueID, transaction?: unknown): Promise<Result<VerifactuRecord, Error>> {
    throw new Error("Method not implemented.");
  }
  // Listado por tenant con criteria saneada
  /* async searchInCompany(criteria: any, companyId: string): Promise<{
    rows: InvoiceListRow[];
    total: number;
    limit: number;
    offset: number;
  }> {
    const { where, order, limit, offset, attributes } = sanitizeListCriteria(criteria);

    // WHERE con scope de company
    const scopedWhere = { ...where, company_id: companyId };

    const options: FindAndCountOptions = {
      where: scopedWhere,
      order,
      limit,
      offset,
      attributes,
      raw: true,     // devolvemos objetos planos -> más rápido
      nest: false,
      distinct: true // por si en el futuro añadimos includes no duplicar count
    };

    const { rows, count } = await VerifactuRecordModel.findAndCountAll(options);

    return {
      rows: rows as unknown as InvoiceListRow[],
      total: typeof count === "number" ? count : (count as any[]).length,
      limit,
      offset,
    };
  } */

  /**
   *
   * Persiste una nueva factura o actualiza una existente.
   *
   * @param invoice - El agregado a guardar.
   * @param transaction - Transacción activa para la operación.
   * @returns Result<VerifactuRecord, Error>
   */
  async save(
    invoice: VerifactuRecord,
    transaction: Transaction
  ): Promise<Result<VerifactuRecord, Error>> {
    try {
      const mapper: IVerifactuRecordDomainMapper = this._registry.getDomainMapper({
        resource: "customer-invoice",
      });
      const mapperData = mapper.mapToPersistence(invoice);

      if (mapperData.isFailure) {
        return Result.fail(mapperData.error);
      }

      const { data } = mapperData;

      const [instance] = await VerifactuRecordModel.upsert(data, { transaction, returning: true });
      const savedInvoice = mapper.mapToDomain(instance);
      return savedInvoice;
    } catch (err: unknown) {
      return Result.fail(translateSequelizeError(err));
    }
  }

  /**
   * Comprueba si existe una factura con un `id` dentro de una `company`.
   *
   * @param companyId - Identificador UUID de la empresa a la que pertenece la factura.
   * @param id - Identificador UUID de la factura.
   * @param transaction - Transacción activa para la operación.
   * @returns Result<boolean, Error>
   */
  async existsByIdInCompany(
    companyId: UniqueID,
    id: UniqueID,
    transaction?: Transaction
  ): Promise<Result<boolean, Error>> {
    try {
      const count = await VerifactuRecordModel.count({
        where: { id: id.toString(), company_id: companyId.toString() },
        transaction,
      });
      return Result.ok(Boolean(count > 0));
    } catch (error: unknown) {
      return Result.fail(translateSequelizeError(error));
    }
  }

  /**
   *
   * Busca una factura por su identificador único.
   *
   * @param companyId - Identificador UUID de la empresa a la que pertenece la factura.
   * @param id - UUID de la factura.
   * @param transaction - Transacción activa para la operación.
   * @returns Result<VerifactuRecord, Error>
   */
  async getByIdInCompany(
    companyId: UniqueID,
    id: UniqueID,
    transaction: Transaction
  ): Promise<Result<VerifactuRecord, Error>> {
    try {
      const mapper: IVerifactuRecordDomainMapper = this._registry.getDomainMapper({
        resource: "customer-invoice",
      });

      const { CustomerModel } = this._database.models;

      const row = await VerifactuRecordModel.findOne({
        where: { id: id.toString(), company_id: companyId.toString() },
        order: [[{ model: VerifactuRecordItemModel, as: "items" }, "position", "ASC"]],
        include: [
          {
            model: CustomerModel,
            as: "current_customer",
            required: false, // false => LEFT JOIN
          },
          {
            model: VerifactuRecordItemModel,
            as: "items",
            required: false,
            include: [
              {
                model: VerifactuRecordItemTaxModel,
                as: "taxes",
                required: false,
              },
            ],
          },
          {
            model: VerifactuRecordTaxModel,
            as: "taxes",
            required: false,
          },
        ],
        transaction,
      });

      if (!row) {
        return Result.fail(new EntityNotFoundError("VerifactuRecord", "id", id.toString()));
      }

      const customer = mapper.mapToDomain(row);
      return customer;
    } catch (err: unknown) {
      return Result.fail(translateSequelizeError(err));
    }
  }

  /**
   *
   * Consulta facturas usando un objeto Criteria (filtros, orden, paginación).
   *
   * @param companyId - Identificador UUID de la empresa a la que pertenece el cliente.
   * @param criteria - Criterios de búsqueda.
   * @param transaction - Transacción activa para la operación.
   * @returns Result<VerifactuRecord[], Error>
   *
   * @see Criteria
   */
  public async findByCriteriaInCompany(
    companyId: UniqueID,
    criteria: Criteria,
    transaction: Transaction
  ): Promise<Result<Collection<VerifactuRecordListDTO>, Error>> {
    try {
      const mapper: IVerifactuRecordListMapper = this._registry.getQueryMapper({
        resource: "customer-invoice",
        query: "LIST",
      });
      const { CustomerModel } = this._database.models;
      const converter = new CriteriaToSequelizeConverter();
      const query = converter.convert(criteria);

      query.where = {
        ...query.where,
        company_id: companyId.toString(),
      };

      query.include = [
        {
          model: CustomerModel,
          as: "current_customer",
          required: false, // false => LEFT JOIN
        },

        {
          model: VerifactuRecordTaxModel,
          as: "taxes",
          required: false,
        },
      ];

      const { rows, count } = await VerifactuRecordModel.findAndCountAll({
        ...query,
        transaction,
      });

      return mapper.mapToDTOCollection(rows, count);
    } catch (err: unknown) {
      return Result.fail(translateSequelizeError(err));
    }
  }

  /**
   *
   * Elimina o marca como eliminada una factura.
   *
   * @param companyId - Identificador UUID de la empresa a la que pertenece el cliente.
   * @param id - UUID de la factura a eliminar.
   * @param transaction - Transacción activa para la operación.
   * @returns Result<void, Error>
   */
  async deleteByIdInCompany(
    companyId: UniqueID,
    id: UniqueID,
    transaction: unknown
  ): Promise<Result<void, Error>> {
    try {
      const deleted = await VerifactuRecordModel.destroy({
        where: { id: id.toString(), company_id: companyId.toString() },
        transaction,
      });

      return Result.ok<void>();
    } catch (err: unknown) {
      return Result.fail(translateSequelizeError(err));
    }
  }
}
