import { DatabaseError } from "@/lib/error";
import { logError } from "@/lib/logger";
import { BaseRepository, RepositoryContext } from "../datastore/repository";

export const ConsoleUserAccessListRepository = (ctx: RepositoryContext) => {
  const { datastore } = new BaseRepository(ctx);

  const FindPromoCodeIdsByUserId = async (userId: string) => {
    try {
      const rows = await datastore
        .selectFrom("console_user_promo_code_access")
        .select("promo_code_id")
        .where("console_user_id", "=", userId)
        .execute();
      return rows.map((r) => r.promo_code_id);
    } catch (err) {
      logError(err);
      throw new DatabaseError({
        error: err,
        message: "Failed fetching user promo code access",
      });
    }
  };

  const FindCampaignIdsByUserId = async (userId: string) => {
    try {
      const rows = await datastore
        .selectFrom("console_user_campaign_access")
        .select("campaign_id")
        .where("console_user_id", "=", userId)
        .execute();
      return rows.map((r) => r.campaign_id);
    } catch (err) {
      logError(err);
      throw new DatabaseError({
        error: err,
        message: "Failed fetching user campaign access",
      });
    }
  };

  const ReplacePromoCodeAccess = async (
    userId: string,
    promoCodeIds: string[],
  ) => {
    try {
      await datastore.transaction().execute(async (trx) => {
        await trx
          .deleteFrom("console_user_promo_code_access")
          .where("console_user_id", "=", userId)
          .execute();
        if (promoCodeIds.length > 0) {
          await trx
            .insertInto("console_user_promo_code_access")
            .values(
              promoCodeIds.map((promoCodeId) => ({
                console_user_id: userId,
                promo_code_id: promoCodeId,
              })),
            )
            .execute();
        }
      });
    } catch (err) {
      logError(err);
      throw new DatabaseError({
        error: err,
        message: "Failed saving user promo code access",
      });
    }
  };

  const ReplaceCampaignAccess = async (
    userId: string,
    campaignIds: string[],
  ) => {
    try {
      await datastore.transaction().execute(async (trx) => {
        await trx
          .deleteFrom("console_user_campaign_access")
          .where("console_user_id", "=", userId)
          .execute();
        if (campaignIds.length > 0) {
          await trx
            .insertInto("console_user_campaign_access")
            .values(
              campaignIds.map((campaignId) => ({
                console_user_id: userId,
                campaign_id: campaignId,
              })),
            )
            .execute();
        }
      });
    } catch (err) {
      logError(err);
      throw new DatabaseError({
        error: err,
        message: "Failed saving user campaign access",
      });
    }
  };

  const FindMemberUserIds = async () => {
    try {
      const rows = await datastore
        .selectFrom("console_user_access_list_members")
        .select("console_user_id")
        .execute();
      return rows.map((r) => r.console_user_id);
    } catch (err) {
      logError(err);
      throw new DatabaseError({
        error: err,
        message: "Failed fetching access list members",
      });
    }
  };

  const AddMember = async (userId: string, addedBy: string | null) => {
    try {
      await datastore
        .insertInto("console_user_access_list_members")
        .values({ console_user_id: userId, added_by: addedBy })
        .onConflict((oc) => oc.column("console_user_id").doNothing())
        .execute();
    } catch (err) {
      logError(err);
      throw new DatabaseError({
        error: err,
        message: "Failed adding user to access list",
      });
    }
  };

  const RemoveMember = async (userId: string) => {
    try {
      await datastore
        .deleteFrom("console_user_access_list_members")
        .where("console_user_id", "=", userId)
        .execute();
    } catch (err) {
      logError(err);
      throw new DatabaseError({
        error: err,
        message: "Failed removing user from access list",
      });
    }
  };

  const FindMemberByUserId = async (userId: string) => {
    try {
      return await datastore
        .selectFrom("console_user_access_list_members")
        .selectAll()
        .where("console_user_id", "=", userId)
        .executeTakeFirst();
    } catch (err) {
      logError(err);
      throw new DatabaseError({
        error: err,
        message: "Failed fetching access list member",
      });
    }
  };

  /**
   * Writes the per-section export settings.
   *
   * Loosely typed because the payload now carries both the legacy `can_export_*`
   * booleans and the `export_mode_*` values, and the mode columns postdate the
   * generated DB types.
   */
  const SetExportFlags = async (
    userId: string,
    flags: Record<string, boolean | string>,
  ) => {
    try {
      await datastore
        .updateTable("console_user_access_list_members")
        .set(flags)
        .where("console_user_id", "=", userId)
        .execute();
    } catch (err) {
      logError(err);
      throw new DatabaseError({
        error: err,
        message: "Failed saving export permissions",
      });
    }
  };

  return {
    FindPromoCodeIdsByUserId,
    FindCampaignIdsByUserId,
    ReplacePromoCodeAccess,
    ReplaceCampaignAccess,
    FindMemberUserIds,
    AddMember,
    RemoveMember,
    FindMemberByUserId,
    SetExportFlags,
  };
};

export type TConsoleUserAccessListRepository = ReturnType<
  typeof ConsoleUserAccessListRepository
>;
