import type Provider from 'oidc-provider';
import type { Repository } from '../../db/repository.js';
import { isDelegationLive, type A2aDelegation } from './delegation.js';
import { logger } from '../../logger.js';
import { A2A_READ_SCOPE } from './oidc-config.js';

export interface A2aPrincipal {
  actingUserId: string;
  clientId: string;
  grantId: string;
  delegation: A2aDelegation;
}

function extractBearer(headers: Record<string, unknown>): string | null {
  const raw = headers['authorization'] ?? headers['Authorization'];
  const v = Array.isArray(raw) ? raw[0] : raw;
  if (typeof v !== 'string') return null;
  const m = /^Bearer\s+(.+)$/i.exec(v.trim());
  return m ? m[1] : null;
}

/**
 * inbound A2A リクエストの認証。fail-closed: いずれか失敗で null。
 * 1) Bearer 抽出 2) provider.AccessToken.find（期限切れは undefined）
 * 3) accountId/grantId 必須 4) a2a.read スコープ必須 5) アカウント active 必須
 * 6) grant_id → 委任 7) 委任が live。
 */
export async function authenticateA2aRequest(
  provider: Provider,
  repo: Repository,
  req: { headers: Record<string, unknown> },
  nowIso: string,
): Promise<A2aPrincipal | null> {
  const token = extractBearer(req.headers);
  if (!token) return null;

  let at: { accountId?: string; grantId?: string; scope?: string } | undefined;
  try {
    at = await provider.AccessToken.find(token);
  } catch (err) {
    logger.warn(`[a2a] token find failed: ${err}`);
    return null;
  }
  if (!at || !at.accountId || !at.grantId) return null;

  // M1: a2a.read スコープが無いトークンは非 A2A（fail-closed）
  const scopes = String(at.scope ?? '').split(' ').filter(Boolean);
  if (!scopes.includes(A2A_READ_SCOPE)) return null;

  // I1: アカウントが active でなければ拒否（無効アカウント・存在しないアカウントも含む）
  const acct = repo.getUserById(at.accountId);
  if (!acct || acct.status !== 'active') return null;

  const row = repo.getA2aDelegationByGrantId(at.grantId);
  if (!row) return null;
  if (!isDelegationLive(row, nowIso)) return null;
  // acting user は委任行の user_id と一致するはず（トークンの accountId と二重確認）
  if (row.userId !== at.accountId) { logger.warn('[a2a] token/delegation user mismatch'); return null; }

  const delegation: A2aDelegation = {
    id: row.id,
    userId: row.userId, clientId: row.clientId, grantId: at.grantId,
    grantedSpaceIds: row.grantedSpaceIds, grantedSkills: row.grantedSkills,
    expiresAt: row.expiresAt, revokedAt: row.revokedAt,
  };
  return { actingUserId: at.accountId, clientId: row.clientId, grantId: at.grantId, delegation };
}
