// @vitest-environment node
import { describe, it, expect, beforeEach } from 'vitest';
import express from 'express';
import request from 'supertest';
import { Repository } from '../../db/repository.js';
import { createA2aRouter, createSpaceA2aAdminRouter } from './a2a-api.js';

function fakeProvider(token: string, payload: { accountId: string; grantId: string; scope?: string }) {
  return { AccessToken: { async find(v: string) { return v === token ? payload : undefined; } } } as any;
}

describe('a2a-api', () => {
  let repo: Repository;

  beforeEach(() => {
    repo = new Repository(':memory:');
    // Seed owner user u1
    (repo as any).db.prepare(
      "INSERT INTO users (id, email, name, avatar_url, role, status, created_at, updated_at) " +
      "VALUES ('u1','u1@test','User One',NULL,'user','active','2026-01-01','2026-01-01')",
    ).run();
    // Seed intruder user u2
    (repo as any).db.prepare(
      "INSERT INTO users (id, email, name, avatar_url, role, status, created_at, updated_at) " +
      "VALUES ('u2','u2@test','User Two',NULL,'user','active','2026-01-01','2026-01-01')",
    ).run();
    // Seed private space s1 owned by u1 (default visibility='private')
    (repo as any).db.prepare(
      "INSERT INTO spaces (id, title, kind, owner_id, visibility) VALUES ('s1','S','case','u1','private')",
    ).run();
    // Seed public space s2 owned by u1 (intruder can see it but can't manage)
    (repo as any).db.prepare(
      "INSERT INTO spaces (id, title, kind, owner_id, visibility) VALUES ('s2','S2','case','u1','public')",
    ).run();
    repo.setSpaceA2aSkills('s1', ['research']);
    repo.createA2aDelegation({
      id: 'd1', userId: 'u1', clientId: 'c', grantId: 'g1',
      grantedSpaceIds: ['s1'], grantedSkills: ['research'],
      audience: 'https://m/a2a', expiresAt: null, revokedAt: null,
    });
  });

  function appWithCard() {
    const app = express();
    app.use(createA2aRouter({
      provider: fakeProvider('tok', { accountId: 'u1', grantId: 'g1', scope: 'openid a2a.read' }),
      repo,
      baseUrl: 'https://m',
      issuer: 'https://m/oidc',
      version: '1.0.0',
    }));
    return app;
  }

  it('serves the base card unauthenticated', async () => {
    const res = await request(appWithCard()).get('/.well-known/agent-card.json');
    expect(res.status).toBe(200);
    expect(res.body.skills).toEqual([]);
    expect(res.body.supportsAuthenticatedExtendedCard).toBe(true);
  });

  it('401 on extended card without bearer', async () => {
    const res = await request(appWithCard()).get('/a2a/agent-card/extended');
    expect(res.status).toBe(401);
  });

  it('extended card scoped to delegation with valid token', async () => {
    const res = await request(appWithCard())
      .get('/a2a/agent-card/extended')
      .set('Authorization', 'Bearer tok');
    expect(res.status).toBe(200);
    expect(res.body.skills.map((s: { id: string }) => s.id)).toEqual(['research']);
  });

  describe('owner allowlist API', () => {
    function makeApp(userId: string) {
      const app = express();
      app.use(express.json());
      app.use((req, _res, next) => { (req as any).user = { id: userId, role: 'user' }; next(); });
      app.use('/api/local/spaces', createSpaceA2aAdminRouter(repo, true));
      return app;
    }

    it('404 (existence hidden) when space is private and caller cannot see it', async () => {
      // s1 is private, owned by u1; u2 (intruder) cannot see it → 404 not 403
      const res = await request(makeApp('u2'))
        .put('/api/local/spaces/s1/a2a-skills')
        .send({ skills: ['research', 'summarize'] });
      expect(res.status).toBe(404);
    });

    it('403 (forbidden) when space is public but caller is not the manager', async () => {
      // s2 is public, owned by u1; u2 can see it but cannot manage → 403
      const res = await request(makeApp('u2'))
        .put('/api/local/spaces/s2/a2a-skills')
        .send({ skills: ['research'] });
      expect(res.status).toBe(403);
    });

    it('200 for owner, skills persisted and deduped', async () => {
      const res = await request(makeApp('u1'))
        .put('/api/local/spaces/s1/a2a-skills')
        .send({ skills: ['research', 'summarize', 'research'] });
      expect(res.status).toBe(200);
      // dedup: 'research' appears once
      expect(res.body.skills).toEqual(['research', 'summarize']);
      expect(repo.getSpaceA2aSkills('s1')).toEqual(['research', 'summarize']);
    });

    it('200 GET for owner', async () => {
      const res = await request(makeApp('u1'))
        .get('/api/local/spaces/s1/a2a-skills');
      expect(res.status).toBe(200);
      expect(res.body.skills).toEqual(['research']);
    });

    it('404 GET when private space not visible to caller', async () => {
      const res = await request(makeApp('u2'))
        .get('/api/local/spaces/s1/a2a-skills');
      expect(res.status).toBe(404);
    });
  });
});
