import type { RepositoryContext } from "@/internal/datastore/repository";
import { MemberAccessRolesRepository } from "@/internal/member-access/member-access-roles.repository";
import { MemberAccessRepository } from "@/internal/member-access/member-access.repository";
import { MemberAccessServices } from "@/internal/member-access/member-access.services";
import { logError } from "@/lib/logger";
import { error as errorResponse, success } from "@/lib/response";
import { resolveMemberAccess } from "@/middlewares/withMemberAccess";
import type { Context } from "hono";

const servicesFor = (c: Context) =>
  MemberAccessServices({
    MemberAccessRepository: MemberAccessRepository(c as RepositoryContext),
    MemberAccessRolesRepository: MemberAccessRolesRepository(
      c as RepositoryContext,
    ),
  });

const capabilitiesFrom = (body: Record<string, unknown>) => ({
  can_view_analytics: asBool(body.can_view_analytics),
  can_view_bookings: asBool(body.can_view_bookings),
  can_view_sessions: asBool(body.can_view_sessions),
  can_view_activity_logs: asBool(body.can_view_activity_logs),
  can_view_saved_searches: asBool(body.can_view_saved_searches),
  can_view_points: asBool(body.can_view_points),
  can_reset_password: asBool(body.can_reset_password),
  can_sync_profile: asBool(body.can_sync_profile),
  account_type_ids: asIds(body.account_type_ids),
  account_status_ids: asIds(body.account_status_ids),
});

const asBool = (value: unknown) => value === true || value === "true";
const asIds = (value: unknown) =>
  Array.isArray(value) ? value.map((entry) => String(entry)) : [];

/** What the signed-in user may do inside the member module. */
export const getMyMemberAccessHandler = async (c: Context) => {
  try {
    const access = await resolveMemberAccess(c);
    return success(c, access, "Member access retrieved successfully");
  } catch (err) {
    logError(err);
    return errorResponse(c, "Failed to resolve member access", 500, err);
  }
};

export const listMemberAccessHandler = async (c: Context) => {
  try {
    const services = servicesFor(c);
    const [rows, assignments] = await Promise.all([
      services.ListAccess(),
      services.ListRoleAssignments(),
    ]);
    // Fold the assignments in so the screen can show who holds which roles
    // without a request per user.
    const byUser = new Map<string, { id: string; name: string }[]>();
    for (const entry of assignments) {
      const list = byUser.get(String(entry.console_user_id)) ?? [];
      list.push({ id: String(entry.role_id), name: entry.role_name });
      byUser.set(String(entry.console_user_id), list);
    }
    const access = rows.map((row) => ({
      ...row,
      roles: byUser.get(String(row.console_user_id)) ?? [],
    }));
    return success(c, { access }, "Member access list retrieved");
  } catch (err) {
    logError(err);
    return errorResponse(c, "Failed to list member access", 500, err);
  }
};

export const getMemberAccessHandler = async (c: Context) => {
  try {
    const consoleUserId = c.req.param("consoleUserId");
    if (!consoleUserId) {
      return errorResponse(c, "Console user id is required", 400);
    }
    // `false` for isSuperAdmin: this reports the stored grant for that user,
    // not the effective access of whoever is asking.
    const access = await servicesFor(c).GetAccess(consoleUserId, false);
    return success(c, access, "Member access retrieved successfully");
  } catch (err) {
    logError(err);
    return errorResponse(c, "Failed to fetch member access", 500, err);
  }
};

/** Roles assigned to one console user, plus the effective access they produce. */
export const getMemberAccessAssignmentsHandler = async (c: Context) => {
  try {
    const consoleUserId = c.req.param("consoleUserId");
    if (!consoleUserId) {
      return errorResponse(c, "Console user id is required", 400);
    }
    const services = servicesFor(c);
    const [roles, effective] = await Promise.all([
      services.ListUserRoles(consoleUserId),
      // `false` for isSuperAdmin: this reports what the assignments grant, not
      // the effective access of whoever is asking.
      services.GetAccess(consoleUserId, false),
    ]);
    return success(c, { roles, effective }, "Assignments retrieved");
  } catch (err) {
    logError(err);
    return errorResponse(c, "Failed to fetch assignments", 500, err);
  }
};

export const assignMemberAccessRoleHandler = async (c: Context) => {
  try {
    const consoleUserId = c.req.param("consoleUserId");
    const body = (await c.req.json().catch(() => null)) as {
      roleId?: string;
    } | null;
    const roleId = String(body?.roleId ?? "");
    if (!consoleUserId || !roleId) {
      return errorResponse(c, "A user id and role id are required", 400);
    }
    const role = await servicesFor(c).AssignRole(
      consoleUserId,
      roleId,
      String(c.get("adminEmail") ?? "") || undefined,
    );
    if (!role) return errorResponse(c, "Role not found", 404);
    c.set("entityId", consoleUserId);
    return success(c, role, "Role assigned");
  } catch (err) {
    logError(err);
    return errorResponse(c, "Failed to assign role", 500, err);
  }
};

export const unassignMemberAccessRoleHandler = async (c: Context) => {
  try {
    const consoleUserId = c.req.param("consoleUserId");
    const roleId = c.req.param("roleId");
    if (!consoleUserId || !roleId) {
      return errorResponse(c, "A user id and role id are required", 400);
    }
    await servicesFor(c).UnassignRole(consoleUserId, roleId);
    c.set("entityId", consoleUserId);
    return success(c, null, "Role removed");
  } catch (err) {
    logError(err);
    return errorResponse(c, "Failed to remove role", 500, err);
  }
};

export const clearMemberAccessHandler = async (c: Context) => {
  try {
    const consoleUserId = c.req.param("consoleUserId");
    if (!consoleUserId) {
      return errorResponse(c, "Console user id is required", 400);
    }
    await servicesFor(c).ClearUserRoles(consoleUserId);
    c.set("entityId", consoleUserId);
    return success(c, null, "All member access removed");
  } catch (err) {
    logError(err);
    return errorResponse(c, "Failed to remove access", 500, err);
  }
};

// ── Named access roles ──────────────────────────────────────────────────────
// Templates, like promo access roles: applying one writes its values onto a
// user's grant. Nothing reads the role at request time.

export const listMemberAccessRolesHandler = async (c: Context) => {
  try {
    const roles = await servicesFor(c).ListRoles();
    return success(c, { roles }, "Member access roles retrieved");
  } catch (err) {
    logError(err);
    return errorResponse(c, "Failed to list member access roles", 500, err);
  }
};

export const getMemberAccessRoleHandler = async (c: Context) => {
  try {
    const id = c.req.param("id");
    if (!id) return errorResponse(c, "Role id is required", 400);
    const role = await servicesFor(c).GetRole(id);
    if (!role) return errorResponse(c, "Role not found", 404);
    return success(c, role, "Member access role retrieved");
  } catch (err) {
    logError(err);
    return errorResponse(c, "Failed to fetch member access role", 500, err);
  }
};

export const createMemberAccessRoleHandler = async (c: Context) => {
  try {
    const body = (await c.req.json()) as Record<string, unknown>;
    const name = String(body.name ?? "").trim();
    if (!name) return errorResponse(c, "A role name is required", 400);
    const role = await servicesFor(c).CreateRole(
      {
        name,
        description: body.description ? String(body.description) : null,
        ...capabilitiesFrom(body),
      },
      String(c.get("adminEmail") ?? "") || undefined,
    );
    c.set("entityId", String(role.id));
    return success(c, role, "Member access role created", 201);
  } catch (err: any) {
    logError(err);
    // The unique index on lower(name) is the guard against duplicates.
    if (String(err?.message ?? "").includes("uq_member_access_roles_name")) {
      return errorResponse(c, "A role with this name already exists", 409);
    }
    return errorResponse(c, "Failed to create member access role", 500, err);
  }
};

export const updateMemberAccessRoleHandler = async (c: Context) => {
  try {
    const id = c.req.param("id");
    if (!id) return errorResponse(c, "Role id is required", 400);
    const body = (await c.req.json()) as Record<string, unknown>;
    const name = String(body.name ?? "").trim();
    if (!name) return errorResponse(c, "A role name is required", 400);
    const role = await servicesFor(c).UpdateRole(id, {
      name,
      description: body.description ? String(body.description) : null,
      ...capabilitiesFrom(body),
    });
    c.set("entityId", id);
    return success(c, role, "Member access role updated");
  } catch (err) {
    logError(err);
    return errorResponse(c, "Failed to update member access role", 500, err);
  }
};

export const deleteMemberAccessRoleHandler = async (c: Context) => {
  try {
    const id = c.req.param("id");
    if (!id) return errorResponse(c, "Role id is required", 400);
    await servicesFor(c).RemoveRole(id);
    c.set("entityId", id);
    return success(c, null, "Member access role deleted");
  } catch (err) {
    logError(err);
    return errorResponse(c, "Failed to delete member access role", 500, err);
  }
};
