import type { Principal } from '@platform-modules/auth';
import { getById, type ContentSchema } from '@platform-modules/content';
import type { Querier } from '@platform-modules/db';
import {
  assertAllowedMenuUrl,
  isMenuAuthorizationError,
  isMenuCycleError,
  isMenuDepthExceededError,
  isMenuLimitExceededError,
  isMenuNotFoundError,
  isMenuUrlSchemeError,
  isMenuValidationError,
  type Actor as MenusActor,
  type MenuItemRow,
  type MenusSchema,
} from '@platform-modules/menus';
import { getFullDb, type DbEnv } from './db.js';
import { jsonError } from './http.js';

/** Menus db handle — reuses the host full-db client typed to MenusSchema. */
export function getMenusDb(env: DbEnv): Querier<MenusSchema> {
  return getFullDb(env).db as unknown as Querier<MenusSchema>;
}

/** Derive menus management capability from role — only admins manage site navigation. */
export function menusActorFromPrincipal(principal: Principal): MenusActor {
  const roles = principal.roles ?? [];
  return {
    id: principal.userId,
    canManageMenus: roles.includes('admin'),
  };
}

export const DEFAULT_PRIMARY: { label: string; href: string }[] = [
  { label: 'Modules', href: '/#modules' },
  { label: 'Blueprint', href: '/blueprint/cms' },
  { label: 'UI Kit', href: '/ui-kit' },
];

export type ResolveMenuHrefContext = {
  db: Querier<ContentSchema>;
};

function menuErrorMessage(error: unknown): string {
  if (error instanceof Error) return error.message;
  return 'Menu request failed.';
}

export function mapMenuError(error: unknown): Response {
  if (isMenuAuthorizationError(error)) {
    return jsonError(403, 'forbidden', menuErrorMessage(error));
  }
  if (isMenuNotFoundError(error)) {
    return jsonError(404, 'not_found', menuErrorMessage(error));
  }
  if (isMenuUrlSchemeError(error) || isMenuValidationError(error)) {
    return jsonError(400, 'validation_error', menuErrorMessage(error));
  }
  if (
    isMenuCycleError(error) ||
    isMenuDepthExceededError(error) ||
    isMenuLimitExceededError(error)
  ) {
    return jsonError(500, 'internal_error', menuErrorMessage(error));
  }
  return jsonError(500, 'internal_error', 'internal error');
}

/**
 * Resolve a menu item to a public href. Entity targets use the anonymous visibility floor
 * (`getById(..., null)` → published+public only). Unresolvable or non-public → null (skip).
 */
export async function resolveMenuHref(
  item: MenuItemRow,
  ctx: ResolveMenuHrefContext,
): Promise<string | null> {
  if (item.targetKind === 'url') {
    if (!item.url) return null;
    try {
      return assertAllowedMenuUrl(item.url);
    } catch {
      return null;
    }
  }

  if (item.targetKind === 'entity') {
    if (!item.targetEntityId) return null;
    const entry = await getById(ctx.db, item.targetEntityId, null);
    if (!entry || entry.type !== 'post') return null;
    return `/post/${entry.slug}`;
  }

  return null;
}
