import { describe, it, expect, beforeAll } from 'vitest';
import http from 'node:http';
import { isMcplocalRunning, mcpctl } from './mcp-client.js';

const MCPLOCAL_URL = process.env['MCPLOCAL_URL'] ?? 'http://localhost:3200';

let available = false;

function fetchJson<T>(path: string): Promise<T | null> {
  return new Promise((resolve) => {
    const req = http.get(`${MCPLOCAL_URL}${path}`, { timeout: 5000 }, (res) => {
      const chunks: Buffer[] = [];
      res.on('data', (chunk: Buffer) => chunks.push(chunk));
      res.on('end', () => {
        try {
          resolve(JSON.parse(Buffer.concat(chunks).toString()) as T);
        } catch {
          resolve(null);
        }
      });
    });
    req.on('error', () => resolve(null));
    req.on('timeout', () => { req.destroy(); resolve(null); });
  });
}

beforeAll(async () => {
  available = await isMcplocalRunning();
});

describe('ProxyModel smoke tests', () => {
  describe('mcplocal /proxymodels endpoint', () => {
    it('GET /proxymodels returns pipelines and plugins', async () => {
      if (!available) return;

      const body = await fetchJson<Array<{ name: string; source: string; type: string }>>('/proxymodels');
      expect(body).not.toBeNull();
      expect(Array.isArray(body)).toBe(true);

      const names = body!.map((m) => m.name);
      // YAML pipelines
      expect(names).toContain('default');
      expect(names).toContain('subindex');
      // TypeScript plugins
      expect(names).toContain('gate');
      expect(names).toContain('content-pipeline');

      // Check types are correct
      const defaultModel = body!.find((m) => m.name === 'default');
      expect(defaultModel!.type).toBe('pipeline');
      const gatePlugin = body!.find((m) => m.name === 'gate');
      expect(gatePlugin!.type).toBe('plugin');
    });

    it('GET /proxymodels/default returns pipeline details', async () => {
      if (!available) return;

      const body = await fetchJson<{ name: string; source: string; type: string; controller: string; stages: unknown[] }>('/proxymodels/default');
      expect(body).not.toBeNull();
      expect(body!.name).toBe('default');
      expect(body!.source).toBe('built-in');
      expect(body!.type).toBe('pipeline');
      expect(Array.isArray(body!.stages)).toBe(true);
    });

    it('GET /proxymodels/gate returns plugin details', async () => {
      if (!available) return;

      const body = await fetchJson<{ name: string; source: string; type: string; hooks: string[] }>('/proxymodels/gate');
      expect(body).not.toBeNull();
      expect(body!.name).toBe('gate');
      expect(body!.type).toBe('plugin');
      expect(Array.isArray(body!.hooks)).toBe(true);
      expect(body!.hooks).toContain('onToolsList');
      expect(body!.hooks).toContain('onInitialize');
    });

    it('GET /proxymodels/nonexistent returns 404', async () => {
      if (!available) return;

      const result = await new Promise<number>((resolve) => {
        const req = http.get(`${MCPLOCAL_URL}/proxymodels/nonexistent`, { timeout: 5000 }, (res) => {
          res.resume();
          resolve(res.statusCode ?? 0);
        });
        req.on('error', () => resolve(0));
        req.on('timeout', () => { req.destroy(); resolve(0); });
      });
      expect(result).toBe(404);
    });
  });

  describe('mcpctl CLI', () => {
    it('mcpctl get proxymodels shows pipelines and plugins', async () => {
      if (!available) return;

      const output = await mcpctl('get proxymodels');
      expect(output).toContain('default');
      expect(output).toContain('subindex');
      expect(output).toContain('gate');
      expect(output).toContain('content-pipeline');
      expect(output).toContain('NAME');
      expect(output).toContain('TYPE');
    });

    it('mcpctl describe proxymodel default shows pipeline type', async () => {
      if (!available) return;

      const output = await mcpctl('describe proxymodel default');
      expect(output).toContain('default');
      expect(output).toContain('built-in');
      expect(output).toContain('pipeline');
    });

    it('mcpctl describe proxymodel gate shows plugin type with hooks', async () => {
      if (!available) return;

      const output = await mcpctl('describe proxymodel gate');
      expect(output).toContain('gate');
      expect(output).toContain('plugin');
      expect(output).toContain('Hooks');
      expect(output).toContain('onToolsList');
    });

    it('mcpctl get projects shows PLUGIN column instead of GATED', async () => {
      if (!available) return;

      const output = await mcpctl('get projects');
      expect(output).toContain('PLUGIN');
      expect(output).not.toContain('GATED');
    });

    it('mcpctl describe project shows Plugin Config section', async () => {
      if (!available) return;

      // Get first project name
      const json = await mcpctl('get projects -o json');
      const projects = JSON.parse(json) as Array<{ name: string }>;
      if (projects.length === 0) return;

      const output = await mcpctl(`describe project ${projects[0]!.name}`);
      expect(output).toContain('Plugin Config');
      expect(output).toContain('Plugin:');
    });

    it('mcpctl get projects -o yaml shows proxyModel and no gated field', async () => {
      if (!available) return;

      const output = await mcpctl('get projects -o yaml');
      // proxyModel should be resolved (not empty)
      expect(output).toContain('proxyModel:');
      expect(output).not.toContain('gated:');
    });

    it('mcpctl get projects -o json shows proxyModel and no gated field', async () => {
      if (!available) return;

      const json = await mcpctl('get projects -o json');
      const projects = JSON.parse(json) as Array<{ proxyModel?: string; gated?: boolean }>;
      expect(projects.length).toBeGreaterThan(0);

      for (const project of projects) {
        expect(project.proxyModel).toBeDefined();
        expect(project.proxyModel).not.toBe('');
        expect(project).not.toHaveProperty('gated');
      }
    });

    it('mcpctl get projects -o yaml is round-trip compatible with apply', async () => {
      if (!available) return;

      const yaml = await mcpctl('get projects -o yaml');
      // Should contain kind and proxyModel (apply-compatible fields)
      expect(yaml).toContain('kind: project');
      expect(yaml).toContain('proxyModel:');
      // Should not contain internal fields
      expect(yaml).not.toContain('ownerId:');
      expect(yaml).not.toContain('createdAt:');
    });
  });
});
