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

export interface MemberAccessCapabilities {
  can_view_analytics: boolean;
  can_view_bookings: boolean;
  can_view_sessions: boolean;
  can_view_activity_logs: boolean;
  can_view_saved_searches: boolean;
  can_view_points: boolean;
  can_reset_password: boolean;
  can_sync_profile: boolean;
  account_type_ids: string[];
  account_status_ids: string[];
}

// Member-module access lives in the console's own DB, alongside the users it
// describes — not in the member DB.
export const MemberAccessRepository = (ctx: RepositoryContext) => {
  const { datastore } = new BaseRepository(ctx);

  const FindByConsoleUser = async (consoleUserId: string) => {
    try {
      const record = await datastore
        .selectFrom("console_user_member_access")
        .selectAll()
        .where("console_user_id", "=", consoleUserId)
        .executeTakeFirst();
      return record ?? null;
    } catch (err) {
      logError(err);
      throw new DatabaseError({
        error: err,
        message: "Failed to fetch member access",
      });
    }
  };

  /**
   * Console users who can reach the member module, with their grant.
   *
   * Two filters matter here. Only *active, non-deleted* users are listed — a
   * disabled account cannot sign in, so configuring its member access is noise.
   * And `has_module_access` reports whether any of the user's roles grants
   * `members:read`: without it the module is unreachable no matter what is
   * granted below, so the screen can lead with the users this actually affects.
   */
  const ListAll = async () => {
    try {
      return await datastore
        .selectFrom("console_users as user")
        .leftJoin(
          "console_user_member_access as access",
          "access.console_user_id",
          "user.id",
        )
        .select((eb) => [
          "user.id as console_user_id",
          "user.email",
          "user.first_name",
          "user.last_name",
          "user.is_super_admin",
          "user.is_active",
          "access.can_view_analytics",
          "access.can_view_bookings",
          "access.can_view_sessions",
          "access.can_view_activity_logs",
          "access.can_view_saved_searches",
          "access.can_view_points",
          "access.can_reset_password",
          "access.can_sync_profile",
          "access.account_type_ids",
          "access.account_status_ids",
          "access.applied_role_id",
          "access.updated_at",
          eb
            .exists(
              eb
                .selectFrom("console_user_roles as user_role")
                .innerJoin(
                  "console_role_privileges as privilege",
                  "privilege.console_role_id",
                  "user_role.console_role_id",
                )
                .innerJoin(
                  "console_features as feature",
                  "feature.id",
                  "privilege.console_feature_id",
                )
                .select("user_role.id")
                .whereRef("user_role.console_user_id", "=", "user.id")
                .where("feature.slug", "=", "members")
                .where("privilege.read", "=", true)
                .where("privilege.is_active", "=", true),
            )
            .as("has_module_access"),
        ])
        .where("user.is_deleted", "=", false)
        .where("user.is_active", "=", true)
        .orderBy("user.email", "asc")
        .execute();
    } catch (err) {
      logError(err);
      throw new DatabaseError({
        error: err,
        message: "Failed to list member access",
      });
    }
  };

  const Upsert = async (
    consoleUserId: string,
    entry: MemberAccessCapabilities,
    appliedRoleId?: string | null,
  ) => {
    try {
      const values = { ...entry, applied_role_id: appliedRoleId ?? null };
      return await datastore
        .insertInto("console_user_member_access")
        .values({ console_user_id: consoleUserId, ...values })
        .onConflict((oc) =>
          oc.column("console_user_id").doUpdateSet({
            ...values,
            updated_at: new Date(),
          }),
        )
        .returningAll()
        .executeTakeFirstOrThrow();
    } catch (err) {
      logError(err);
      throw new DatabaseError({
        error: err,
        message: "Failed to save member access",
      });
    }
  };

  const DeleteForConsoleUser = async (consoleUserId: string) => {
    try {
      await datastore
        .deleteFrom("console_user_member_access")
        .where("console_user_id", "=", consoleUserId)
        .execute();
    } catch (err) {
      logError(err);
      throw new DatabaseError({
        error: err,
        message: "Failed to remove member access",
      });
    }
  };

  return { FindByConsoleUser, ListAll, Upsert, DeleteForConsoleUser };
};

export type TMemberAccessRepository = ReturnType<typeof MemberAccessRepository>;
