// Tests for src/connections.js — credential management
const fs = require('fs');

jest.mock('../src/constants');
const { CONNECTIONS_FILE } = require('../src/constants');
const connections = require('../src/connections');

describe('connections', () => {
  beforeEach(() => {
    fs.writeFileSync(CONNECTIONS_FILE, '{}');
  });

  describe('setApiKey', () => {
    test('saves an API key', () => {
      connections.setApiKey('github', 'ghp_test123');
      const data = JSON.parse(fs.readFileSync(CONNECTIONS_FILE, 'utf-8'));
      expect(data.github_key).toBe('ghp_test123');
    });

    test('key appears as active in listConnections', () => {
      connections.setApiKey('brave_search', 'BSA-test');
      const list = connections.listConnections();
      expect(list.find(c => c.service === 'brave_search').active).toBe(true);
    });
  });

  describe('removeConnection', () => {
    test('removes connection data', () => {
      connections.setApiKey('github', 'ghp_xxx');
      connections.removeConnection('github');
      const data = JSON.parse(fs.readFileSync(CONNECTIONS_FILE, 'utf-8'));
      expect(data.github_key).toBeUndefined();
    });
  });

  describe('listConnections', () => {
    test('returns array of all services', () => {
      const list = connections.listConnections();
      expect(Array.isArray(list)).toBe(true);
      expect(list.length).toBeGreaterThan(5);
      const services = list.map(c => c.service);
      expect(services).toContain('github');
      expect(services).toContain('telegram');
    });

    test('masks API keys', () => {
      connections.setApiKey('github', 'ghp_verylongtoken123456');
      const gh = connections.listConnections().find(c => c.service === 'github');
      expect(gh.masked).not.toBe('ghp_verylongtoken123456');
      expect(gh.masked).toContain('...');
    });
  });

  describe('setJiraConfig', () => {
    test('saves Jira URL and token', () => {
      connections.setJiraConfig('https://jira.test', 'pat-123');
      const d = JSON.parse(fs.readFileSync(CONNECTIONS_FILE, 'utf-8'));
      expect(d.jira_url).toBe('https://jira.test');
      expect(d.jira_token).toBe('pat-123');
    });
  });

  describe('setConfluenceConfig', () => {
    test('saves Confluence URL and token', () => {
      connections.setConfluenceConfig('https://wiki.test', 'pat-456');
      const d = JSON.parse(fs.readFileSync(CONNECTIONS_FILE, 'utf-8'));
      expect(d.confluence_url).toBe('https://wiki.test');
    });
  });

  describe('setServiceNowBasicConfig', () => {
    test('saves ServiceNow basic auth credentials', () => {
      connections.setServiceNowBasicConfig('https://dev12345.service-now.com/', 'svc.user', 'p@ss');
      const d = JSON.parse(fs.readFileSync(CONNECTIONS_FILE, 'utf-8'));
      expect(d.servicenow_url).toBe('https://dev12345.service-now.com');
      expect(d.servicenow_auth_type).toBe('basic');
      expect(d.servicenow_username).toBe('svc.user');
      expect(d.servicenow_password).toBe('p@ss');
    });

    test('switching from OAuth to basic clears stale tokens', () => {
      connections.setServiceNowOAuthConfig('https://dev12345.service-now.com', 'cid', 'cs');
      // Simulate a previously-issued access token
      const data = JSON.parse(fs.readFileSync(CONNECTIONS_FILE, 'utf-8'));
      data.servicenow_access_token = 'should-be-gone';
      data.servicenow_refresh_token = 'should-be-gone';
      fs.writeFileSync(CONNECTIONS_FILE, JSON.stringify(data));
      connections.setServiceNowBasicConfig('https://dev12345.service-now.com', 'u', 'p');
      const after = JSON.parse(fs.readFileSync(CONNECTIONS_FILE, 'utf-8'));
      expect(after.servicenow_access_token).toBeUndefined();
      expect(after.servicenow_refresh_token).toBeUndefined();
    });
  });

  describe('setServiceNowOAuthConfig', () => {
    test('saves OAuth client credentials and clears basic creds', () => {
      connections.setServiceNowBasicConfig('https://dev12345.service-now.com', 'u', 'p');
      connections.setServiceNowOAuthConfig('https://dev12345.service-now.com', 'client-id', 'client-secret');
      const d = JSON.parse(fs.readFileSync(CONNECTIONS_FILE, 'utf-8'));
      expect(d.servicenow_auth_type).toBe('oauth');
      expect(d.servicenow_client_id).toBe('client-id');
      expect(d.servicenow_client_secret).toBe('client-secret');
      expect(d.servicenow_username).toBeUndefined();
      expect(d.servicenow_password).toBeUndefined();
    });
  });

  describe('removeConnection servicenow', () => {
    test('strips both basic and OAuth fields', () => {
      connections.setServiceNowOAuthConfig('https://dev12345.service-now.com', 'cid', 'cs');
      const data = JSON.parse(fs.readFileSync(CONNECTIONS_FILE, 'utf-8'));
      data.servicenow_access_token = 'tok';
      data.servicenow_refresh_token = 'ref';
      data.servicenow_token_expiry = 9999;
      fs.writeFileSync(CONNECTIONS_FILE, JSON.stringify(data));
      connections.removeConnection('servicenow');
      const after = JSON.parse(fs.readFileSync(CONNECTIONS_FILE, 'utf-8'));
      expect(after.servicenow_url).toBeUndefined();
      expect(after.servicenow_client_id).toBeUndefined();
      expect(after.servicenow_access_token).toBeUndefined();
    });
  });

  describe('listConnections includes servicenow', () => {
    test('inactive by default', () => {
      const sn = connections.listConnections().find(c => c.service === 'servicenow');
      expect(sn).toBeDefined();
      expect(sn.active).toBe(false);
    });

    test('active when basic credentials set', () => {
      connections.setServiceNowBasicConfig('https://dev12345.service-now.com', 'u', 'p');
      const sn = connections.listConnections().find(c => c.service === 'servicenow');
      expect(sn.active).toBe(true);
      expect(sn.authType).toBe('basic');
    });

    test('not active until OAuth refresh token is granted', () => {
      connections.setServiceNowOAuthConfig('https://dev12345.service-now.com', 'cid', 'cs');
      let sn = connections.listConnections().find(c => c.service === 'servicenow');
      expect(sn.active).toBe(false);
      expect(sn.hasCredentials).toBe(true);
      // Simulate completed OAuth flow
      const data = JSON.parse(fs.readFileSync(CONNECTIONS_FILE, 'utf-8'));
      data.servicenow_refresh_token = 'ref';
      fs.writeFileSync(CONNECTIONS_FILE, JSON.stringify(data));
      sn = connections.listConnections().find(c => c.service === 'servicenow');
      expect(sn.active).toBe(true);
    });
  });

  describe('setZoomConfig', () => {
    test('saves Zoom S2S credentials', () => {
      connections.setZoomConfig('acc', 'cli', 'sec');
      const d = JSON.parse(fs.readFileSync(CONNECTIONS_FILE, 'utf-8'));
      expect(d.zoom_account_id).toBe('acc');
      expect(d.zoom_client_id).toBe('cli');
      expect(d.zoom_client_secret).toBe('sec');
    });
  });

  describe('setD365Config', () => {
    test('saves D365 credentials', () => {
      connections.setD365Config('https://org.crm.dynamics.com', 't1', 'c2', 's3');
      const d = JSON.parse(fs.readFileSync(CONNECTIONS_FILE, 'utf-8'));
      expect(d.d365_url).toBe('https://org.crm.dynamics.com');
      expect(d.d365_tenant_id).toBe('t1');
    });
  });

  describe('setTelegramChatId', () => {
    test('saves chat ID', () => {
      connections.setTelegramChatId('12345');
      const d = JSON.parse(fs.readFileSync(CONNECTIONS_FILE, 'utf-8'));
      expect(d.telegram_chat_id).toBe('12345');
    });
  });

  describe('Gemini provider', () => {
    test('setProvider stores gemini config and getProvider returns it', () => {
      connections.setProvider({
        type: 'gemini',
        api_key: 'AIzaTEST',
        model: 'gemini-3.5-flash',
        max_tokens: 8192,
      });
      const active = connections.getProvider();
      expect(active.type).toBe('gemini');
      expect(active.model).toBe('gemini-3.5-flash');
      expect(active.api_key).toBe('AIzaTEST');
      expect(active.max_tokens).toBe(8192);
      // Default endpoint should be injected when not provided
      expect(active.base_url).toBe('https://generativelanguage.googleapis.com/v1beta');
    });

    test('honors explicit base_url override (Vertex AI proxy)', () => {
      connections.setProvider({
        type: 'gemini',
        base_url: 'https://my-vertex-proxy.example.com/v1',
        api_key: 'k',
        model: 'gemini-3.5-pro',
      });
      const active = connections.getProvider();
      expect(active.base_url).toBe('https://my-vertex-proxy.example.com/v1');
    });

    test('saved gemini config persists alongside other providers', () => {
      connections.setProvider({ type: 'gemini', api_key: 'g-key', model: 'gemini-3.5-flash' });
      connections.setProvider({ type: 'openai', base_url: 'https://api.openai.com/v1', api_key: 'sk-x', model: 'gpt-4o' });
      const all = connections.getAllProviders();
      expect(all.active).toBe('openai');
      expect(all.gemini.api_key).toBe('g-key');
      expect(all.gemini.model).toBe('gemini-3.5-flash');
    });

    test('removeProvider reverts active but preserves saved gemini config', () => {
      connections.setProvider({ type: 'gemini', api_key: 'g-key', model: 'gemini-3.5-flash' });
      connections.removeProvider();
      const all = connections.getAllProviders();
      expect(all.active).toBe('ollama');
      expect(all.gemini.api_key).toBe('g-key');
    });

    test('requires model when type=gemini', () => {
      expect(() => connections.setProvider({ type: 'gemini', api_key: 'k' })).toThrow(/model is required/);
    });
  });
});
