import { describe, it, expect, afterEach } from 'vitest';
import { readCredentials, generateAuthUrl, saveToken } from './gmail-auth.js';
import { writeFileSync, mkdirSync, rmSync, readFileSync, statSync } from 'fs';
import { join } from 'path';
import { tmpdir } from 'os';

describe('gmail-auth', () => {
  const testDir = join(tmpdir(), 'flynn-test-gmail-auth');

  afterEach(() => {
    try { rmSync(testDir, { recursive: true }); } catch {}
  });

  describe('readCredentials', () => {
    it('reads installed credentials', () => {
      mkdirSync(testDir, { recursive: true });
      const credPath = join(testDir, 'creds.json');
      writeFileSync(credPath, JSON.stringify({
        installed: {
          client_id: 'test-id',
          client_secret: 'test-secret',
          redirect_uris: ['http://localhost:3000'],
        },
      }));

      const creds = readCredentials(credPath);
      expect(creds.client_id).toBe('test-id');
      expect(creds.client_secret).toBe('test-secret');
      expect(creds.redirect_uris).toEqual(['http://localhost:3000']);
    });

    it('reads web credentials', () => {
      mkdirSync(testDir, { recursive: true });
      const credPath = join(testDir, 'creds.json');
      writeFileSync(credPath, JSON.stringify({
        web: {
          client_id: 'web-id',
          client_secret: 'web-secret',
        },
      }));

      const creds = readCredentials(credPath);
      expect(creds.client_id).toBe('web-id');
      expect(creds.client_secret).toBe('web-secret');
    });

    it('throws when file does not exist', () => {
      expect(() => readCredentials('/nonexistent/creds.json')).toThrow('not found');
    });

    it('throws when client_id is missing', () => {
      mkdirSync(testDir, { recursive: true });
      const credPath = join(testDir, 'creds.json');
      writeFileSync(credPath, JSON.stringify({ installed: { client_secret: 's' } }));

      expect(() => readCredentials(credPath)).toThrow('missing client_id or client_secret');
    });

    it('throws when client_secret is missing', () => {
      mkdirSync(testDir, { recursive: true });
      const credPath = join(testDir, 'creds.json');
      writeFileSync(credPath, JSON.stringify({ installed: { client_id: 'id' } }));

      expect(() => readCredentials(credPath)).toThrow('missing client_id or client_secret');
    });
  });

  describe('generateAuthUrl', () => {
    it('generates correct OAuth2 URL', () => {
      const url = generateAuthUrl('my-client-id', 'my-secret', 'http://localhost:3000');
      expect(url).toContain('https://accounts.google.com/o/oauth2/v2/auth');
      expect(url).toContain('client_id=my-client-id');
      expect(url).toContain('redirect_uri=http%3A%2F%2Flocalhost%3A3000');
      expect(url).toContain('scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.readonly');
      expect(url).toContain('access_type=offline');
      expect(url).toContain('prompt=consent');
    });
  });

  describe('saveToken', () => {
    it('saves token as JSON with 0o600 permissions', () => {
      mkdirSync(testDir, { recursive: true });
      const tokenPath = join(testDir, 'token.json');
      const token = { access_token: 'abc', refresh_token: 'def' };

      saveToken(tokenPath, token);

      const saved = JSON.parse(readFileSync(tokenPath, 'utf-8'));
      expect(saved).toEqual(token);

      const stats = statSync(tokenPath);
      // Check owner-only read/write (0o600)
      expect(stats.mode & 0o777).toBe(0o600);
    });

    it('creates parent directories if needed', () => {
      const tokenPath = join(testDir, 'nested', 'dir', 'token.json');
      saveToken(tokenPath, { token: 'value' });

      const saved = JSON.parse(readFileSync(tokenPath, 'utf-8'));
      expect(saved).toEqual({ token: 'value' });
    });
  });
});
