// Credentials git (P12) : round-trip SecretBox, résumé SANS secret, NULLification à la suppression.
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { openDb, type Db } from '../src/db/index.js';
import { SecretBox } from '../src/core/secret-box.js';
import { GitCredentialsManager } from '../src/core/git-credentials.js';

let db: Db;
let mgr: GitCredentialsManager;
const SECRET = 'pat-super-secret-1234';

beforeEach(() => {
  db = openDb(':memory:');
  mgr = new GitCredentialsManager(db, new SecretBox(Buffer.alloc(32, 7)));
});
afterEach(() => db.close());

describe('GitCredentialsManager', () => {
  it('crée un credential : secret chiffré en DB, résumé sans secret', () => {
    const cred = mgr.create({ label: 'gh', service: 'github', authType: 'pat', secret: SECRET });
    expect(cred.hasSecret).toBe(true);
    expect(cred.secretLast4).toBe('1234');
    // le résumé sérialisé ne contient JAMAIS le secret en clair.
    expect(JSON.stringify(cred)).not.toContain(SECRET);
    // en DB : valeur chiffrée (préfixe v1:), jamais le clair.
    const row = db.prepare('SELECT secret_encrypted FROM git_credentials WHERE id = ?').get(cred.id) as { secret_encrypted: string };
    expect(row.secret_encrypted.startsWith('v1:')).toBe(true);
    expect(row.secret_encrypted).not.toContain(SECRET);
  });

  it('getSecret déchiffre (round-trip)', () => {
    const cred = mgr.create({ label: 'gl', service: 'gitlab', authType: 'pat', secret: SECRET });
    expect(mgr.getSecret(cred.id)).toBe(SECRET);
  });

  it('exige un base_url pour Gitea', () => {
    expect(() => mgr.create({ label: 'x', service: 'gitea', authType: 'pat', secret: SECRET })).toThrow();
    expect(mgr.create({ label: 'x', service: 'gitea', authType: 'pat', baseUrl: 'https://git.example.com', secret: SECRET }).baseUrl).toBe('https://git.example.com');
  });

  it('refuse ssh_key/oauth pour l’instant (P12a)', () => {
    expect(() => mgr.create({ label: 'x', service: 'github', authType: 'ssh_key', secret: SECRET })).toThrow();
    expect(() => mgr.create({ label: 'x', service: 'github', authType: 'oauth', secret: SECRET })).toThrow();
  });

  it('remove() NULLifie repos.credential_id (pas de FK)', () => {
    const cred = mgr.create({ label: 'gh', service: 'github', authType: 'pat', secret: SECRET });
    db.prepare(
      "INSERT INTO repos (id, path, label, post_create_hooks, pre_trust, created_at, hidden, credential_id) VALUES ('r1', '/tmp/r1', 'r1', '[]', 0, '2020-01-01T00:00:00Z', 0, ?)",
    ).run(cred.id);
    expect(mgr.remove(cred.id)).toBe(true);
    const repo = db.prepare("SELECT credential_id FROM repos WHERE id = 'r1'").get() as { credential_id: string | null };
    expect(repo.credential_id).toBeNull();
  });
});
