import { UniqueID } from "@/core/common/domain";
import { SequelizeRepository } from "@/core/common/infrastructure";
import { Result } from "@repo/rdx-utils";
import { Op, Sequelize, Transaction } from "sequelize";
import { ITabContextRepository, TabContext } from "../../domain/";
import { ITabContextMapper } from "../mappers";
import { TabContextModel } from "./tab-context.model";

export class TabContextRepository
  extends SequelizeRepository<TabContext>
  implements ITabContextRepository
{
  private readonly _mapper!: ITabContextMapper;

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

    return null;
  }

  constructor(database: Sequelize, mapper: ITabContextMapper) {
    super(database);
    this._mapper = mapper;
  }

  async getContextByTabId(
    tabId: UniqueID,
    transaction?: Transaction
  ): Promise<Result<TabContext, Error>> {
    try {
      const rawContext = await this._getBy(
        TabContextModel,
        "tab_id",
        tabId.toString(),
        {},
        transaction
      );

      if (!rawContext === true) {
        return Result.fail(new Error("Tab context not exists"));
      }

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

  async contextExistsByTabId(
    tabId: UniqueID,
    transaction?: Transaction
  ): Promise<Result<boolean, Error>> {
    try {
      const result = await this._exists(TabContextModel, "tab_id", tabId.toString(), transaction);

      return Result.ok(Boolean(result));
    } catch (error: unknown) {
      return this._handleDatabaseError(error, this._customErrorMapper);
    }
  }

  /**
   * Registra un contexto para un tab id o actualiza si ya existe
   * @param context
   * @param transaction
   * @returns
   */

  async registerContextByTabId(
    context: TabContext,
    transaction?: Transaction
  ): Promise<Result<void, Error>> {
    try {
      const { userId, tabId } = context;
      const persistenceData = this._mapper.mapToPersistence(context);

      // Si existe el contexto de ese tabId, lo actualizo.

      if (await this._exists(TabContextModel, "tab_id", tabId.toString())) {
        await TabContextModel.update(persistenceData, {
          where: { [Op.and]: [{ tab_id: tabId.toString() }, { user_id: userId.toString() }] },
          transaction,
        });
      } else {
        await TabContextModel.create(persistenceData, {
          include: [{ all: true }],
          transaction,
        });
      }

      return Result.ok();
    } catch (error: unknown) {
      return this._handleDatabaseError(error, this._customErrorMapper);
    }
  }

  /**
   * Desregistra un contexto para un tab id o actualiza si ya existe
   * @param context
   * @param transaction
   * @returns
   */

  async unregisterContextByTabId(
    context: TabContext,
    transaction?: Transaction
  ): Promise<Result<void, Error>> {
    try {
      const { userId, tabId } = context;

      await TabContextModel.destroy({
        where: { [Op.and]: [{ tab_id: tabId.toString() }, { user_id: userId.toString() }] },
        transaction,
      });
      return Result.ok();
    } catch (error: unknown) {
      return this._handleDatabaseError(error, this._customErrorMapper);
    }
  }
}
