import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdtempSync, rmSync, statSync, existsSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { FileSecretStore } from '../../src/secrets/file-store.js';

let tempDir: string;

beforeEach(() => {
  tempDir = mkdtempSync(join(tmpdir(), 'mcpctl-secrets-test-'));
});

afterEach(() => {
  rmSync(tempDir, { recursive: true, force: true });
});

describe('FileSecretStore', () => {
  it('returns null for missing key', async () => {
    const store = new FileSecretStore({ configDir: tempDir });
    expect(await store.get('nonexistent')).toBeNull();
  });

  it('stores and retrieves a secret', async () => {
    const store = new FileSecretStore({ configDir: tempDir });
    await store.set('api-key', 'sk-12345');
    expect(await store.get('api-key')).toBe('sk-12345');
  });

  it('overwrites existing values', async () => {
    const store = new FileSecretStore({ configDir: tempDir });
    await store.set('api-key', 'old-value');
    await store.set('api-key', 'new-value');
    expect(await store.get('api-key')).toBe('new-value');
  });

  it('stores multiple keys', async () => {
    const store = new FileSecretStore({ configDir: tempDir });
    await store.set('key-a', 'value-a');
    await store.set('key-b', 'value-b');
    expect(await store.get('key-a')).toBe('value-a');
    expect(await store.get('key-b')).toBe('value-b');
  });

  it('deletes a key', async () => {
    const store = new FileSecretStore({ configDir: tempDir });
    await store.set('api-key', 'sk-12345');
    expect(await store.delete('api-key')).toBe(true);
    expect(await store.get('api-key')).toBeNull();
  });

  it('returns false when deleting nonexistent key', async () => {
    const store = new FileSecretStore({ configDir: tempDir });
    expect(await store.delete('nonexistent')).toBe(false);
  });

  it('sets 0600 permissions on secrets file', async () => {
    const store = new FileSecretStore({ configDir: tempDir });
    await store.set('api-key', 'sk-12345');
    const stat = statSync(join(tempDir, 'secrets'));
    expect(stat.mode & 0o777).toBe(0o600);
  });

  it('creates config dir if missing', async () => {
    const nested = join(tempDir, 'sub', 'dir');
    const store = new FileSecretStore({ configDir: nested });
    await store.set('api-key', 'sk-12345');
    expect(existsSync(join(nested, 'secrets'))).toBe(true);
  });

  it('recovers from corrupted JSON', async () => {
    writeFileSync(join(tempDir, 'secrets'), 'NOT JSON!!!', 'utf-8');
    const store = new FileSecretStore({ configDir: tempDir });
    // Should not throw, returns null for any key
    expect(await store.get('api-key')).toBeNull();
    // Should be able to write over corrupted file
    await store.set('api-key', 'fresh-value');
    expect(await store.get('api-key')).toBe('fresh-value');
  });

  it('reports file backend', () => {
    const store = new FileSecretStore({ configDir: tempDir });
    expect(store.backend()).toBe('file');
  });

  it('preserves other keys on delete', async () => {
    const store = new FileSecretStore({ configDir: tempDir });
    await store.set('key-a', 'value-a');
    await store.set('key-b', 'value-b');
    await store.delete('key-a');
    expect(await store.get('key-a')).toBeNull();
    expect(await store.get('key-b')).toBe('value-b');
  });
});
