import { logError } from "@/lib/logger";
import { SSEManager } from "@/internal/sse/sse.manager";
import { TConsoleRolesRepository } from "./console-roles.repository";
import type { TConsoleUserRolesServices } from "@/internal/console-user-roles/console-user-roles.services";
import type { TConsoleUserSessionServices } from "@/internal/console-user-sessions/console-user-session.services";

type TConsoleRoleServiceDeps = {
  ConsoleRolesRepository: TConsoleRolesRepository;
  ConsoleUserRolesServices?: TConsoleUserRolesServices;
  ConsoleUserSessionServices?: TConsoleUserSessionServices;
};

export const ConsoleRoleServices = ({
  ConsoleRolesRepository,
  ConsoleUserRolesServices,
  ConsoleUserSessionServices,
}: TConsoleRoleServiceDeps) => {
  const CreateRole = async (entry: {
    name: string;
    description: string;
    privileges?: {
      featureId: string;
      create: boolean;
      read: boolean;
      update: boolean;
      delete: boolean;
    }[];
  }) => {
    try {
      const existing = await ConsoleRolesRepository.FindEntryByRoleName(
        entry.name,
      );
      if (existing) {
        throw new Error("Role already exists");
      }
      const newRecord = await ConsoleRolesRepository.CreateEntry(entry);
      return newRecord;
    } catch (err) {
      logError(err);
      throw new Error((err as Error).message ?? "Failed creating role");
    }
  };

  const FindRoleById = async (id: string) => {
    try {
      const record = await ConsoleRolesRepository.FindEntryById(id);
      return record;
    } catch (err) {
      logError(err);
      throw new Error((err as Error).message ?? "Failed finding role");
    }
  };

  const UpdateRole = async (
    id: string,
    entry: {
      name?: string;
      description?: string;
      privileges?: {
        featureId: string;
        create: boolean;
        read: boolean;
        update: boolean;
        delete: boolean;
      }[];
    },
  ) => {
    try {
      if (entry.name) {
        const existing = await ConsoleRolesRepository.FindByRoleNameAndNotId(
          id,
          entry.name,
        );
        if (existing) {
          throw new Error("Role name already in use");
        }
      }
      const updated = await ConsoleRolesRepository.UpdateEntryById(id, entry);

      // Session-only invalidation: delete all sessions for users who have this role
      try {
        if (ConsoleUserRolesServices && ConsoleUserSessionServices) {
          const userRoleEntries = await ConsoleUserRolesServices.FindUsersByRoleId(id);
          if (Array.isArray(userRoleEntries) && userRoleEntries.length > 0) {
            // dedupe user ids
            const userIds = Array.from(
              new Set(userRoleEntries.map((r: any) => r.console_user_id)),
            );
            for (const uid of userIds) {
              await ConsoleUserSessionServices.DeleteSessionByUserId(uid);
              try {
                // push a force-logout event to any connected clients for this user
                SSEManager.send(uid, "force-logout", { reason: "role-updated" });
              } catch (e) {
                // don't fail role update on push errors
                logError(e);
              }
            }
          }
        }
      } catch (err) {
        logError(err);
      }

      return updated;
    } catch (err) {
      logError(err);
      throw new Error((err as Error).message ?? "Failed updating role");
    }
  };

  const FindRoles = async (entry?: {
    page?: number;
    pageSize?: number;
    searchTerm?: string;
  }) => {
    try {
      const records = await ConsoleRolesRepository.FindEntries(entry);
      return records;
    } catch (err) {
      logError(err);
      throw new Error((err as Error).message ?? "Failed finding roles");
    }
  };

  return {
    CreateRole,
    FindRoleById,
    UpdateRole,
    FindRoles,
  };
};
export type TConsoleRoleServices = ReturnType<typeof ConsoleRoleServices>;
