import {
  sanitizePayload,
  sanitizeQueryParams,
} from "@/internal/middlewares/sanitzePayload";
import { withConsoleUserRole } from "@/middlewares/console-role";
import { withConsoleUserSession } from "@/middlewares/withConsoleUserSession";
import { Hono } from "hono";

import {
  updateMyProfileHandler,
  uploadMyAvatarHandler,
} from "@/controllers/admin/console-users/console-user-profile.controller";
import { ConsoleUserSessionRepository } from "@/internal/console-user-sessions/console-user-session.repository";
import { ConsoleUserSessionServices } from "@/internal/console-user-sessions/console-user-session.services";
import { SSEManager } from "@/internal/sse/sse.manager";
import { CONSOLE_USER_SESSION_ID_TOKEN_DEFAULTS } from "@/lib/cookie";
import { getCookie } from "hono/cookie";
import { V1Controllers } from "./controllers";
import { UpdateUserAccessListSchema } from "./controllers/console-user-access-list.schema";
import {
  CreateConsoleFeatureSchema,
  CreateRoleSchema,
  SearchRoleSchema,
  UpdateRoleSchema,
} from "./controllers/console-user-roles.schema";
import {
  RegisterConsoleUserSchema,
  RequestPasswordResetSchema,
  RotateConsoleUserSessionSchema,
  SearchConsoleUserSchema,
  SetPasswordSchema,
  SigninConsoleUserSchema,
  SsoSigninConsoleUserSchema,
  UpdateConsoleUserSchema,
} from "./controllers/console-users.schema";

export const AdminRoutes = new Hono();

// ConsoleUser

AdminRoutes.post(
  "/admin/console-user/register",
  withConsoleUserSession(),
  withConsoleUserRole("user-management", "create"),
  sanitizePayload(RegisterConsoleUserSchema),
  async (ctx) => V1Controllers(ctx).ConsoleUserController.RegisterConsoleUser(),
);

AdminRoutes.post(
  "/admin/console-user/signin",
  sanitizePayload(SigninConsoleUserSchema),
  async (ctx) => V1Controllers(ctx).ConsoleUserController.SigninConsoleUser(),
);

/*
 * Redeeming a one-time sign-in link from an email. Unauthenticated by necessity —
 * the token is the credential — and single-use, so it cannot be replayed.
 */
AdminRoutes.post(
  "/admin/console-user/sso-signin",
  sanitizePayload(SsoSigninConsoleUserSchema),
  async (ctx) =>
    V1Controllers(ctx).ConsoleUserController.SsoSigninConsoleUser(),
);

AdminRoutes.patch(
  "/admin/console-user/session-rotate",
  sanitizePayload(RotateConsoleUserSessionSchema),
  async (ctx) =>
    V1Controllers(ctx).ConsoleUserController.RotateConsoleUserSession(),
);

AdminRoutes.delete("/admin/console-user/session-revoke", async (ctx) =>
  V1Controllers(ctx).ConsoleUserController.SignoutHandler(),
);

AdminRoutes.get("/admin/console-user/check-super-admin", async (ctx) =>
  V1Controllers(ctx).ConsoleUserController.CheckSuperAdminExists(),
);

AdminRoutes.post(
  "/admin/console-user/super-admin",
  sanitizePayload(RegisterConsoleUserSchema),
  async (ctx) =>
    V1Controllers(ctx).ConsoleUserController.RegisterInitialSuperAdmin(),
);

AdminRoutes.get(
  "/admin/console-user",
  withConsoleUserSession(),
  withConsoleUserRole("user-management", "read"),
  sanitizeQueryParams(SearchConsoleUserSchema),
  async (ctx) => V1Controllers(ctx).ConsoleUserController.SearchConsoleUser(),
);

AdminRoutes.put(
  "/admin/console-user/:id",
  withConsoleUserSession(),
  withConsoleUserRole("user-management", "update"),
  sanitizePayload(UpdateConsoleUserSchema),
  async (ctx) => V1Controllers(ctx).ConsoleUserController.UpdateConsoleUser(),
);

/*
 * Self-service profile.
 *
 * Session-gated only — no `user-management` role: these edit the caller's own
 * record, and the handlers ignore anything that isn't a profile field, so a user
 * can't promote themselves or change their roles here.
 */
AdminRoutes.put(
  "/admin/console-user/me/profile",
  withConsoleUserSession(),
  updateMyProfileHandler,
);

AdminRoutes.post(
  "/admin/console-user/me/avatar",
  withConsoleUserSession(),
  uploadMyAvatarHandler,
);

AdminRoutes.get(
  "/admin/console-user/me",
  withConsoleUserSession(),
  async (ctx) => V1Controllers(ctx).ConsoleUserController.Me(),
);

AdminRoutes.get(
  "/admin/console-user/:id",
  withConsoleUserSession(),
  withConsoleUserRole("user-management", "read"),
  async (ctx) => V1Controllers(ctx).ConsoleUserController.FindConsoleUser(),
);

// Role Management
AdminRoutes.post(
  "/admin/console-user-roles/feature",
  withConsoleUserSession(),
  sanitizePayload(CreateConsoleFeatureSchema),
  async (ctx) => V1Controllers(ctx).ConsoleUserRoleController.CreateFeature(),
);

AdminRoutes.get(
  "/admin/console-user-roles/feature",
  withConsoleUserSession(),
  withConsoleUserRole("role-management", "read"),
  async (ctx) => V1Controllers(ctx).ConsoleUserRoleController.GetAllFeatures(),
);

AdminRoutes.post(
  "/admin/console-user-roles/role",
  withConsoleUserSession(),
  withConsoleUserRole("role-management", "create"),
  sanitizePayload(CreateRoleSchema),
  async (ctx) => V1Controllers(ctx).ConsoleUserRoleController.CreateRole(),
);

AdminRoutes.get(
  "/admin/console-user-roles",
  withConsoleUserSession(),
  withConsoleUserRole("role-management", "read"),
  sanitizeQueryParams(SearchRoleSchema),
  async (ctx) => V1Controllers(ctx).ConsoleUserRoleController.SearchRoles(),
);

AdminRoutes.get(
  "/admin/console-user-roles/:id",
  withConsoleUserSession(),
  withConsoleUserRole("role-management", "read"),
  async (ctx) => V1Controllers(ctx).ConsoleUserRoleController.FindRoleById(),
);

AdminRoutes.put(
  "/admin/console-user-roles/role/:id",
  withConsoleUserSession(),
  withConsoleUserRole("role-management", "update"),
  sanitizePayload(UpdateRoleSchema),
  async (ctx) => V1Controllers(ctx).ConsoleUserRoleController.UpdateRole(),
);

AdminRoutes.get(
  "/admin/console-user-access-list",
  withConsoleUserSession(),
  withConsoleUserRole("promocode-access-list", "read"),
  sanitizeQueryParams(SearchConsoleUserSchema),
  async (ctx) =>
    V1Controllers(ctx).ConsoleUserAccessListController.ListUsersForAccessList(),
);

AdminRoutes.get(
  "/admin/console-user-access-list/candidates",
  withConsoleUserSession(),
  withConsoleUserRole("promocode-access-list", "read"),
  sanitizeQueryParams(SearchConsoleUserSchema),
  async (ctx) =>
    V1Controllers(
      ctx,
    ).ConsoleUserAccessListController.ListAccessListCandidates(),
);

AdminRoutes.post(
  "/admin/console-user-access-list/members/:id",
  withConsoleUserSession(),
  withConsoleUserRole("promocode-access-list", "create"),
  async (ctx) =>
    V1Controllers(ctx).ConsoleUserAccessListController.AddUserToAccessList(),
);

AdminRoutes.delete(
  "/admin/console-user-access-list/members/:id",
  withConsoleUserSession(),
  withConsoleUserRole("promocode-access-list", "delete"),
  async (ctx) =>
    V1Controllers(
      ctx,
    ).ConsoleUserAccessListController.RemoveUserFromAccessList(),
);

AdminRoutes.get(
  "/admin/console-user-access-list/me",
  withConsoleUserSession(),
  async (ctx) =>
    V1Controllers(ctx).ConsoleUserAccessListController.GetMyExportPermissions(),
);

AdminRoutes.get(
  "/admin/console-user-access-list/:id",
  withConsoleUserSession(),
  withConsoleUserRole("promocode-access-list", "read"),
  async (ctx) =>
    V1Controllers(ctx).ConsoleUserAccessListController.GetUserAccessList(),
);

AdminRoutes.put(
  "/admin/console-user-access-list/:id",
  withConsoleUserSession(),
  withConsoleUserRole("promocode-access-list", "update"),
  sanitizePayload(UpdateUserAccessListSchema),
  async (ctx) =>
    V1Controllers(ctx).ConsoleUserAccessListController.UpdateUserAccessList(),
);

AdminRoutes.post(
  "/admin/console-user/set-password",
  sanitizePayload(SetPasswordSchema),
  async (ctx) => V1Controllers(ctx).ConsoleUserController.SetPassword(),
);

AdminRoutes.post(
  "/admin/console-user/request-password-reset",
  sanitizePayload(RequestPasswordResetSchema),
  async (ctx) =>
    V1Controllers(ctx).ConsoleUserController.RequestPasswordReset(),
);

// SSE endpoint for admin clients to receive push events (force-logout, etc.)
AdminRoutes.get("/internal/sse", async (ctx) => {
  const consoleUserSessionServices = ConsoleUserSessionServices({
    ConsoleUserSessionRepository: ConsoleUserSessionRepository(ctx as any),
  });

  const admin_session_id = getCookie(
    ctx,
    CONSOLE_USER_SESSION_ID_TOKEN_DEFAULTS.name,
  );
  if (!admin_session_id) {
    return ctx.json({ success: false, message: "Unauthorized" }, 401);
  }

  const session =
    await consoleUserSessionServices.GetUserSession(admin_session_id);
  if (!session || !session.consoleUserRecord) {
    return ctx.json({ success: false, message: "Unauthorized" }, 401);
  }

  const userId = session.consoleUserRecord.id;
  const stream = new ReadableStream<string>({
    start(controller) {
      controller.enqueue(": connected\n\n");
      SSEManager.add(userId, controller as any);
    },
    cancel(controller) {
      SSEManager.remove(userId, controller as any);
    },
  });

  const headers = new Headers({
    "Content-Type": "text/event-stream",
    "Cache-Control": "no-cache",
    Connection: "keep-alive",
  });

  return new Response(stream, { headers });
});

// Admin Activity Logs
AdminRoutes.get(
  "/admin/activity-logs",
  withConsoleUserSession(),
  withConsoleUserRole("admin-logs" as any, "read"),
  async (ctx) => V1Controllers(ctx).AdminActivityLogController.SearchLogs(),
);

AdminRoutes.post(
  "/admin/activity-logs",
  withConsoleUserSession(),
  async (ctx) => V1Controllers(ctx).AdminActivityLogController.WriteLog(),
);

AdminRoutes.delete(
  "/admin/activity-logs",
  withConsoleUserSession(),
  withConsoleUserRole("admin-logs" as any, "delete"),
  async (ctx) => V1Controllers(ctx).AdminActivityLogController.ClearAllLogs(),
);
