import type { TConsoleUserAccessListServices } from "@/internal/console-user-access-list/console-user-access-list.services";
import type { TConsoleUserServices } from "@/internal/console-users/console-users.services";
import { logError } from "@/lib/logger";
import { error, success } from "@/lib/response";
import type { Context } from "hono";
import type z from "zod";
import type { SearchConsoleUserSchema } from "./console-users.schema";
import type { UpdateUserAccessListSchema } from "./console-user-access-list.schema";

type TConsoleUserAccessListControllerDeps = {
  ConsoleUserAccessListServices: TConsoleUserAccessListServices;
  ConsoleUserServices: TConsoleUserServices;
};

export const ConsoleUserAccessListController = (
  ctx: Context,
  { ConsoleUserAccessListServices, ConsoleUserServices }: TConsoleUserAccessListControllerDeps,
) => {
  const universalExclusions = async () => {
    const consoleUserId = ctx.get("consoleUserId") as string | undefined;
    const superAdmins = await ConsoleUserServices.GetSuperAdmins();
    return Array.from(
      new Set([
        ...(consoleUserId ? [consoleUserId] : []),
        ...superAdmins.map((u) => u.id),
      ]),
    );
  };

  // The main Access List table — only users a super admin has explicitly
  // added (console_user_access_list_members), not the whole directory.
  const ListUsersForAccessList = async () => {
    try {
      const raw: z.infer<typeof SearchConsoleUserSchema> = await ctx.req.query();
      const [excludeUserIds, memberIds] = await Promise.all([
        universalExclusions(),
        ConsoleUserAccessListServices.FindMemberUserIds(),
      ]);
      const consoleUsers = await ConsoleUserServices.FindAllConsoleUsers({
        page: parseInt(raw.page ?? "1"),
        pageSize: parseInt(raw?.pageSize ?? "50"),
        searchTerm: raw?.searchTerm,
        includeUserIds: memberIds,
        excludeUserIds,
      });
      return success(ctx, consoleUsers);
    } catch (err) {
      logError(err);
      return error(ctx, undefined, undefined, err);
    }
  };

  const ListAccessListCandidates = async () => {
    try {
      const raw: z.infer<typeof SearchConsoleUserSchema> = await ctx.req.query();
      const [universal, memberIds] = await Promise.all([
        universalExclusions(),
        ConsoleUserAccessListServices.FindMemberUserIds(),
      ]);
      const excludeUserIds = Array.from(new Set([...universal, ...memberIds]));
      const consoleUsers = await ConsoleUserServices.FindAllConsoleUsers({
        page: parseInt(raw.page ?? "1"),
        pageSize: parseInt(raw?.pageSize ?? "50"),
        searchTerm: raw?.searchTerm,
        excludeUserIds,
        onlyActive: true,
      });
      return success(ctx, consoleUsers);
    } catch (err) {
      logError(err);
      return error(ctx, undefined, undefined, err);
    }
  };

  const AddUserToAccessList = async () => {
    try {
      const { id } = ctx.req.param();
      const addedBy = (ctx.get("adminEmail") as string | undefined) ?? null;
      await ConsoleUserAccessListServices.AddUserToAccessList(id, addedBy);
      return success(ctx, { id }, "User added to access list");
    } catch (err) {
      logError(err);
      return error(ctx, undefined, undefined, err);
    }
  };

  const RemoveUserFromAccessList = async () => {
    try {
      const { id } = ctx.req.param();
      await ConsoleUserAccessListServices.RemoveUserFromAccessList(id);
      return success(ctx, { id }, "User removed from access list");
    } catch (err) {
      logError(err);
      return error(ctx, undefined, undefined, err);
    }
  };

  // Bundles the target user's basic profile and export permissions in with
  // their access grants, so the frontend doesn't need a second call to
  // /admin/console-user/:id (which requires the broader user-management
  // permission this feature must not).
  const GetUserAccessList = async () => {
    try {
      const { id } = ctx.req.param();
      const [user, access, exportPermissions] = await Promise.all([
        ConsoleUserServices.GetUserById(id),
        ConsoleUserAccessListServices.GetUserAccess(id),
        ConsoleUserAccessListServices.GetExportPermissions(id),
      ]);
      return success(ctx, { user, ...access, ...exportPermissions });
    } catch (err) {
      logError(err);
      return error(ctx, undefined, undefined, err);
    }
  };

  const UpdateUserAccessList = async () => {
    try {
      const { id } = ctx.req.param();
      const raw: z.infer<typeof UpdateUserAccessListSchema> =
        await ctx.req.json();
      const updated = await ConsoleUserAccessListServices.SetUserAccess(
        id,
        raw,
      );
      return success(ctx, updated);
    } catch (err) {
      logError(err);
      return error(ctx, undefined, undefined, err);
    }
  };

  // Any logged-in user can check their OWN export permissions — this is what
  // gates the Export buttons on Campaigns/Promo Codes/Member Referrals/
  // Reports for everyone, not just Access List managers, so it can't require
  // the promocode-access-list permission itself.
  const GetMyExportPermissions = async () => {
    try {
      const consoleUserId = ctx.get("consoleUserId") as string | undefined;
      if (!consoleUserId) {
        // No session user => unrestricted, so every section downloads directly.
        return success(ctx, {
          isMember: false,
          canExportCampaigns: true,
          canExportPromoCodes: true,
          canExportMemberReferrals: true,
          canExportReports: true,
          exportModes: {
            campaigns: "download",
            promo_codes: "download",
            member_referrals: "download",
            reports: "download",
            bookings: "download",
          },
        });
      }
      const permissions =
        await ConsoleUserAccessListServices.GetExportPermissions(consoleUserId);
      return success(ctx, permissions);
    } catch (err) {
      logError(err);
      return error(ctx, undefined, undefined, err);
    }
  };

  return {
    ListUsersForAccessList,
    ListAccessListCandidates,
    AddUserToAccessList,
    RemoveUserFromAccessList,
    GetUserAccessList,
    UpdateUserAccessList,
    GetMyExportPermissions,
  };
};
