import { redirectToLoginAfterSessionExpired } from "@/features/auth/service/auth-service";
import type {
  ApiPermission,
  ApiResponse,
  ApiRole,
  ApiRoleDetail,
  Permission,
  PermissionActionKey,
  PermissionMatrixItem,
  PermissionMatrixResponse,
  PermissionMatrixResource,
  Role,
  RoleDetail,
  SaveRolePayload,
} from "@/features/role/role-data";

const API_URL =
  process.env.NEXT_PUBLIC_API_URL || "http://localhost:3001/api/v1";

function formatWords(value: string) {
  return value
    .replace(/_/g, " ")
    .split(" ")
    .filter(Boolean)
    .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
    .join(" ");
}

function countValue(value: unknown[] | number | undefined) {
  if (Array.isArray(value)) {
    return value.length;
  }

  return typeof value === "number" ? value : 0;
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null;
}

const PERMISSION_ACTION_KEYS: PermissionActionKey[] = [
  "read",
  "create",
  "update",
  "delete",
  "export",
];

function getErrorMessage(body: unknown, status: number) {
  if (isRecord(body)) {
    const message = body.message;

    if (Array.isArray(message)) return message.join(" ");
    if (typeof message === "string" && message.trim()) return message;
  }

  if (status === 400) return "Please check the form and try again.";
  if (status === 403) return "You do not have permission to do this.";
  if (status === 404) return "Role not found.";
  if (status === 409) return "A role with this name already exists.";

  return `Request failed (${status}).`;
}

// fetch wrapper that surfaces the backend's error message (baseAPI swallows it).
async function request<T>(path: string, options: RequestInit = {}): Promise<T> {
  const res = await fetch(`${API_URL}${path}`, {
    ...options,
    credentials: "include",
    headers: { "Content-Type": "application/json", ...options.headers },
  });

  const text = await res.text();
  let body: unknown = null;

  if (text) {
    try {
      body = JSON.parse(text);
    } catch {
      body = text;
    }
  }

  if (!res.ok) {
    if (res.status === 401) {
      redirectToLoginAfterSessionExpired();
    }

    throw new Error(getErrorMessage(body, res.status));
  }

  return body as T;
}

function mapApiRole(apiRole: ApiRole): Role {
  return {
    id: apiRole.id,
    role: formatWords(apiRole.name),
    guardName: formatWords(apiRole.guardName),
    status: apiRole.deletedAt ? "Inactive" : "Active",
    permissions: apiRole._count?.permissions ?? countValue(apiRole.permissions),
    resources: apiRole._count?.resources ?? countValue(apiRole.resources),
    updated: apiRole.updatedAt,
  };
}

export async function getRoles(): Promise<Role[]> {
  const response = await request<ApiResponse<ApiRole[]>>("/roles", {
    method: "GET",
  });

  return Array.isArray(response.data) ? response.data.map(mapApiRole) : [];
}

export async function getPermissions(): Promise<Permission[]> {
  const response = await request<ApiResponse<unknown>>("/permissions", {
    method: "GET",
  });

  const list = Array.isArray(response.data)
    ? response.data
    : isRecord(response.data) && Array.isArray(response.data.items)
      ? response.data.items
      : [];

  return (list as ApiPermission[])
    .filter((item) => isRecord(item))
    .map((item) => ({ id: Number(item.id), name: String(item.name ?? "") }))
    .filter((item) => Number.isFinite(item.id) && item.name.length > 0);
}

function mapPermissionMatrixItem(
  value: unknown,
): PermissionMatrixItem | undefined {
  if (!isRecord(value)) return undefined;

  const id = Number(value.id);
  const name = String(value.name ?? "");
  const label = String(value.label ?? "");

  if (!Number.isFinite(id) || !name || !label) {
    return undefined;
  }

  return { id, name, label };
}

function mapPermissionMatrixResource(
  value: unknown,
): PermissionMatrixResource | undefined {
  if (!isRecord(value)) return undefined;

  const resource = String(value.resource ?? "");
  const label = String(value.label ?? "");
  const order = Number(value.order);
  const permissions: PermissionMatrixResource["permissions"] = {};
  const rawPermissions = isRecord(value.permissions) ? value.permissions : {};

  if (!resource || !label || !Number.isFinite(order)) {
    return undefined;
  }

  for (const actionKey of PERMISSION_ACTION_KEYS) {
    const permission = mapPermissionMatrixItem(rawPermissions[actionKey]);
    if (permission) {
      permissions[actionKey] = permission;
    }
  }

  return { resource, label, order, permissions };
}

export async function getPermissionMatrix(): Promise<PermissionMatrixResponse> {
  const response = await request<ApiResponse<unknown>>("/permissions/matrix", {
    method: "GET",
  });

  const resources =
    isRecord(response.data) && Array.isArray(response.data.resources)
      ? response.data.resources
      : [];

  return {
    resources: resources
      .map(mapPermissionMatrixResource)
      .filter((resource): resource is PermissionMatrixResource =>
        Boolean(resource),
      ),
  };
}

export async function getRoleDetail(id: number): Promise<RoleDetail> {
  const response = await request<ApiResponse<ApiRoleDetail>>(`/roles/${id}`, {
    method: "GET",
  });

  const role = response.data;

  return {
    id: role.id,
    name: role.name,
    guardName: role.guardName,
    permissions: (role.permissions ?? []).map((permission) => ({
      id: permission.id,
      name: permission.name,
      guardName: permission.guardName,
    })),
    permissionIds: (role.permissions ?? []).map((permission) => permission.id),
  };
}

export async function createRole(payload: SaveRolePayload): Promise<void> {
  await request("/roles", {
    method: "POST",
    body: JSON.stringify(payload),
  });
}

export async function updateRole(
  id: number,
  payload: SaveRolePayload,
): Promise<void> {
  await request(`/roles/${id}`, {
    method: "PATCH",
    body: JSON.stringify(payload),
  });
}

export async function deleteRole(id: number): Promise<void> {
  await request(`/roles/${id}`, {
    method: "DELETE",
  });
}
