import express, { Router } from 'express';
import { canManageSpace } from './visibility.js';
import { viewerOf } from './space-viewer.js';
import { logger } from '../logger.js';
import { parseToolPolicy } from '../engine/workspace-tool-policy.js';
import {
  listToolCategories,
  SENSITIVE_CATEGORIES,
  SENSITIVE_TOOLS,
} from '../engine/tools/tool-categories.js';
import type { SpaceApiDeps } from './space-api.js';

/** スペースのツールポリシー GET/PUT。createSpaceApi から分離（巨大関数分割）。挙動は等価。 */
export function registerSpaceToolPolicyRoutes(router: Router, deps: SpaceApiDeps): void {
  const { repo } = deps;
  const jsonParser = express.json({ limit: deps.uploadLimitMb ? `${deps.uploadLimitMb}mb` : '50mb' });

  // ─── ツールポリシー ─────────────────────────────────────────────────────────
  //
  // GET: スペース可視者（viewer 以上）が読める。
  // PUT: canManageSpace（owner / admin / member.role==='owner'）のみ書ける。
  //
  // 返却形状（GET / PUT 共通）:
  //   { policy: WorkspaceToolPolicy,
  //     categories: { name, sensitive, enabled }[],
  //     sensitiveTools: { name, enabled }[] }
  //
  // 未知のカテゴリ/ツール名を enabledSensitive / disabledSafe に含めると 400。
  // 既知かどうかの判定: SENSITIVE_CATEGORIES ∪ listToolCategories() の和集合 +
  // SENSITIVE_TOOLS を allowlist として使う。

  async function buildPolicyResponse(spaceId: string) {
    const json = repo.getSpaceToolPolicy(spaceId);
    const policy = parseToolPolicy(json);
    const allCats = await listToolCategories();
    const disabledSafe = new Set(policy.disabledSafe ?? []);
    const enabledSensitive = new Set(policy.enabledSensitive ?? []);

    const categories = allCats.map((name) => {
      const sensitive = SENSITIVE_CATEGORIES.has(name);
      const enabled = sensitive ? enabledSensitive.has(name) : !disabledSafe.has(name);
      return { name, sensitive, enabled };
    });

    const sensitiveTools = [...SENSITIVE_TOOLS].map((name) => ({
      name,
      enabled: enabledSensitive.has(name),
    }));

    return { policy, categories, sensitiveTools };
  }

  // 既知のカテゴリ+ツール名の allowlist を組み立てる（validation 用）
  async function buildKnownSensitiveNames(): Promise<Set<string>> {
    const allCats = await listToolCategories();
    const known = new Set<string>(SENSITIVE_CATEGORIES);
    for (const c of allCats) known.add(c); // all cats are valid candidates
    for (const t of SENSITIVE_TOOLS) known.add(t);
    return known;
  }

  // GET /:id/tool-policy — viewer 可
  router.get('/:id/tool-policy', async (req, res) => {
    const viewer = viewerOf(req, deps.authActive);
    const space = await repo.getSpace(req.params.id, { viewer });
    if (!space) return res.status(404).json({ error: 'not found' });
    try {
      const body = await buildPolicyResponse(space.id);
      res.json(body);
    } catch (err) {
      logger.error(`[space-api] tool-policy GET failed space=${space.id} err=${(err as Error).message}`);
      res.status(500).json({ error: 'internal error' });
    }
  });

  // PUT /:id/tool-policy — canManageSpace 必須
  router.put('/:id/tool-policy', jsonParser, async (req, res) => {
    const viewer = viewerOf(req, deps.authActive);
    const space = await repo.getSpace(req.params.id, { viewer });
    if (!space) return res.status(404).json({ error: 'not found' });
    const memberRole = repo.getSpaceMemberRole(space.id, viewer.id);
    if (!canManageSpace(viewer, { ownerId: space.ownerId }, memberRole)) {
      return res.status(403).json({ error: 'forbidden' });
    }

    const { disabledSafe, enabledSensitive } = req.body ?? {};

    // Validate: both fields must be arrays of strings if present
    if (disabledSafe !== undefined && !Array.isArray(disabledSafe)) {
      return res.status(400).json({ error: 'disabledSafe must be an array' });
    }
    if (enabledSensitive !== undefined && !Array.isArray(enabledSensitive)) {
      return res.status(400).json({ error: 'enabledSensitive must be an array' });
    }
    if (disabledSafe && disabledSafe.some((x: unknown) => typeof x !== 'string')) {
      return res.status(400).json({ error: 'disabledSafe must be an array of strings' });
    }
    if (enabledSensitive && enabledSensitive.some((x: unknown) => typeof x !== 'string')) {
      return res.status(400).json({ error: 'enabledSensitive must be an array of strings' });
    }

    // Validate: unknown category/tool names → 400
    const known = await buildKnownSensitiveNames();
    if (disabledSafe) {
      const unknown = (disabledSafe as string[]).filter((n) => !known.has(n));
      if (unknown.length > 0) {
        return res.status(400).json({ error: `unknown category names in disabledSafe: ${unknown.join(', ')}` });
      }
    }
    if (enabledSensitive) {
      const unknown = (enabledSensitive as string[]).filter((n) => !known.has(n));
      if (unknown.length > 0) {
        return res.status(400).json({ error: `unknown names in enabledSensitive: ${unknown.join(', ')}` });
      }
    }

    const cleanPolicy = {
      ...(disabledSafe && disabledSafe.length > 0 ? { disabledSafe: disabledSafe as string[] } : {}),
      ...(enabledSensitive && enabledSensitive.length > 0 ? { enabledSensitive: enabledSensitive as string[] } : {}),
    };

    try {
      repo.setSpaceToolPolicy(space.id, JSON.stringify(cleanPolicy));
      const body = await buildPolicyResponse(space.id);
      res.json(body);
    } catch (err) {
      logger.error(`[space-api] tool-policy PUT failed space=${space.id} err=${(err as Error).message}`);
      res.status(500).json({ error: 'internal error' });
    }
  });
}
