import { describe, it, expect, beforeEach } from 'vitest';
import { Repository } from '../../db/repository.js';
import { makeOidcAdapterFactory } from './oidc-adapter.js';

describe('oidc SQLite adapter', () => {
  let repo: Repository;
  let factory: (name: string) => any;
  beforeEach(() => { repo = new Repository(':memory:'); factory = makeOidcAdapterFactory(repo); });

  it('upsert/find round-trips a payload and respects expiry semantics', async () => {
    const a = factory('AccessToken');
    await a.upsert('t1', { jti: 't1', grantId: 'g1' }, 3600);
    expect(await a.find('t1')).toMatchObject({ jti: 't1' });
  });

  it('find returns undefined after expiry', async () => {
    const a = factory('AccessToken');
    // expiresIn 0 は「無期限」ではなく即時。adapter は expiresIn 秒後を expires_at に入れる。
    await a.upsert('t2', { jti: 't2' }, -1); // 過去
    expect(await a.find('t2')).toBeUndefined();
  });

  it('consume marks payload.consumed', async () => {
    const a = factory('AuthorizationCode');
    await a.upsert('c1', { foo: 1 }, 600);
    await a.consume('c1');
    expect((await a.find('c1')).consumed).toBeTypeOf('number');
  });

  it('revokeByGrantId removes grant artifacts', async () => {
    const at = factory('AccessToken');
    await at.upsert('t3', { jti: 't3', grantId: 'g9' }, 600);
    await at.revokeByGrantId('g9');
    expect(await at.find('t3')).toBeUndefined();
  });

  it('Client model resolves from a2a_clients into oidc client metadata', async () => {
    repo.createA2aClient({
      clientId: 'cli1', name: 'T', redirectUris: ['https://x/cb'],
      secretHash: 'h', tokenEndpointAuthMethod: 'client_secret_basic',
      agentCardUrl: null, issuer: null, keyFingerprint: null, status: 'active', createdBy: 'a',
    });
    const c = factory('Client');
    const meta = await c.find('cli1');
    expect(meta).toMatchObject({
      client_id: 'cli1',
      redirect_uris: ['https://x/cb'],
      grant_types: ['authorization_code'],
      response_types: ['code'],
    });
  });

  it('Client model returns undefined for disabled client', async () => {
    repo.createA2aClient({
      clientId: 'cli2', name: 'T', redirectUris: ['https://x/cb'],
      secretHash: 'h', tokenEndpointAuthMethod: 'client_secret_basic',
      agentCardUrl: null, issuer: null, keyFingerprint: null, status: 'disabled', createdBy: 'a',
    });
    expect(await factory('Client').find('cli2')).toBeUndefined();
  });

  it('findByUid returns payload for fresh Session', async () => {
    const s = factory('Session');
    await s.upsert('sess1', { uid: 'uid-fresh', data: { foo: 'bar' } }, 3600);
    const result = await s.findByUid('uid-fresh');
    expect(result).toMatchObject({ uid: 'uid-fresh', data: { foo: 'bar' } });
  });

  it('findByUid returns undefined for expired Session', async () => {
    const s = factory('Session');
    await s.upsert('sess2', { uid: 'uid-expired', data: { foo: 'baz' } }, -1);
    const result = await s.findByUid('uid-expired');
    expect(result).toBeUndefined();
  });

  it('findByUserCode returns payload for fresh entry', async () => {
    const ac = factory('AuthorizationCode');
    await ac.upsert('code1', { userCode: 'uc-fresh', data: { sub: 'user123' } }, 600);
    const result = await ac.findByUserCode('uc-fresh');
    expect(result).toMatchObject({ userCode: 'uc-fresh', data: { sub: 'user123' } });
  });

  it('findByUserCode returns undefined for expired entry', async () => {
    const ac = factory('AuthorizationCode');
    await ac.upsert('code2', { userCode: 'uc-expired', data: { sub: 'user456' } }, -1);
    const result = await ac.findByUserCode('uc-expired');
    expect(result).toBeUndefined();
  });
});
