/**
 * A2A Agent Card ルータ + オーナー公開許可リスト API
 *
 * createA2aRouter  — GET /.well-known/agent-card.json (公開)
 *                    GET /a2a/agent-card/extended      (Bearer 認証)
 * createSpaceA2aAdminRouter — GET/PUT /:id/a2a-skills (owner/admin のみ)
 */

import { Router } from 'express';
import express from 'express';
import type Provider from 'oidc-provider';
import type { Repository } from '../../db/repository.js';
import { authenticateA2aRequest } from './token-auth.js';
import { buildBaseCard, buildExtendedCard } from './agent-card.js';
import { computeEffectiveScope } from './delegation.js';
import { canManageSpace } from '../visibility.js';
import { viewerOf } from '../space-viewer.js';
import { logger } from '../../logger.js';

export interface A2aRouterDeps {
  provider: Provider;
  repo: Repository;
  baseUrl: string;
  issuer: string;
  version: string;
}

/**
 * Base card ルート (unauthenticated public) と
 * extended card ルート (A2A Bearer token 必須) を提供するルータ。
 *
 * これらのパスは /api 配下ではないため、requireAuth の包括ゲートに入らない。
 * Base card は完全公開。Extended card は authenticateA2aRequest で自己認証する。
 */
export function createA2aRouter(deps: A2aRouterDeps): Router {
  const router = Router();

  // ── Base card: 未認証で取得可能 ──────────────────────────────────────
  router.get('/.well-known/agent-card.json', (_req, res) => {
    const card = buildBaseCard({
      baseUrl: deps.baseUrl,
      issuer: deps.issuer,
      version: deps.version,
    });
    res.json(card);
  });

  // ── Extended card: valid A2A Bearer token 必須 ────────────────────────
  router.get('/a2a/agent-card/extended', async (req, res) => {
    try {
      const nowIso = new Date().toISOString();
      // IncomingHttpHeaders は Record<string,unknown> にキャスト可能
      const principal = await authenticateA2aRequest(
        deps.provider,
        deps.repo,
        { headers: req.headers as unknown as Record<string, unknown> },
        nowIso,
      );
      if (!principal) {
        res.status(401).json({ error: 'unauthorized' });
        return;
      }

      // acting user の可視スペース・スキルを delegation の AND 交差で算出（IDOR 防止）
      const scope = await computeEffectiveScope(deps.repo, principal);
      if (!scope) {
        res.status(401).json({ error: 'unknown or inactive principal' });
        return;
      }

      // 委任スコープと可視スペースの AND 交差で extended card を構築
      const card = buildExtendedCard(principal, {
        baseUrl: deps.baseUrl,
        issuer: deps.issuer,
        version: deps.version,
        userVisibleSpaceIds: scope.userVisibleSpaceIds,
        allowlistsBySpace: scope.allowlistsBySpace,
      });

      // 監査ログ（best-effort — 失敗してもカードは返す）
      try {
        await deps.repo.addAuditLog(null, 'a2a.card.extended', principal.actingUserId, {
          clientId: principal.clientId,
        });
      } catch {
        // intentionally swallowed
      }

      res.json(card);
    } catch (err) {
      logger.warn(`[a2a] extended card failed: ${err}`);
      res.status(500).json({ error: 'internal error' });
    }
  });

  return router;
}

/**
 * スペース A2A 公開スキル許可リスト管理 API。
 * /api/local/spaces にマウントすること（requireAuth 配下）。
 *
 * GET  /:id/a2a-skills — 現在の許可リストを返す
 * PUT  /:id/a2a-skills — 許可リストを上書き更新
 *
 * 両エンドポイントとも owner または admin のみ操作可能。
 */
export function createSpaceA2aAdminRouter(repo: Repository, authActive: boolean): Router {
  const router = Router();
  const jsonParser = express.json();

  // GET /:id/a2a-skills
  router.get('/:id/a2a-skills', async (req, res) => {
    const viewer = viewerOf(req, authActive);

    // space-api.ts と同様: getSpace({viewer}) でアクセス不可 or 非存在 → 404,
    // 見えるが管理権限なし → 403 (existence hidden)
    const space = await repo.getSpace(req.params.id, { viewer });
    if (!space) {
      res.status(404).json({ error: 'not found' });
      return;
    }
    const memberRole = repo.getSpaceMemberRole(space.id, viewer.id);
    if (!canManageSpace(viewer, { ownerId: space.ownerId }, memberRole)) {
      res.status(403).json({ error: 'forbidden' });
      return;
    }
    res.json({ skills: repo.getSpaceA2aSkills(space.id) });
  });

  // PUT /:id/a2a-skills
  router.put('/:id/a2a-skills', jsonParser, async (req, res) => {
    const viewer = viewerOf(req, authActive);

    const space = await repo.getSpace(req.params.id, { viewer });
    if (!space) {
      res.status(404).json({ error: 'not found' });
      return;
    }
    const memberRole = repo.getSpaceMemberRole(space.id, viewer.id);
    if (!canManageSpace(viewer, { ownerId: space.ownerId }, memberRole)) {
      res.status(403).json({ error: 'forbidden' });
      return;
    }

    const { skills: rawSkills } = (req.body ?? {}) as { skills?: unknown };
    if (!Array.isArray(rawSkills) || rawSkills.some(s => typeof s !== 'string')) {
      res.status(400).json({ error: 'skills must be an array of strings' });
      return;
    }
    const skills = [...new Set(rawSkills as string[])];
    repo.setSpaceA2aSkills(space.id, skills);
    try {
      await repo.addAuditLog(null, 'a2a.space.skills.set', viewer.id, { spaceId: req.params.id, skills });
    } catch {
      // best-effort — failure must not block the response
    }
    res.json({ skills });
  });

  return router;
}
