import { describe, it, expect, vi, beforeEach } from 'vitest';
import { readFileSync, writeFileSync } from 'node:fs';
import yaml from 'js-yaml';
import { createEditCommand } from '../../src/commands/edit.js';
import type { ApiClient } from '../../src/api-client.js';

function mockClient(): ApiClient {
  return {
    get: vi.fn(async () => ({})),
    post: vi.fn(async () => ({})),
    put: vi.fn(async () => ({})),
    delete: vi.fn(async () => {}),
  } as unknown as ApiClient;
}

describe('edit command', () => {
  let client: ReturnType<typeof mockClient>;
  let output: string[];
  const log = (...args: unknown[]) => output.push(args.map(String).join(' '));

  beforeEach(() => {
    client = mockClient();
    output = [];
  });

  it('fetches server, opens editor, applies changes on save', async () => {
    // GET /api/v1/servers returns list for resolveNameOrId
    vi.mocked(client.get).mockImplementation(async (path: string) => {
      if (path === '/api/v1/servers') {
        return [{ id: 'srv-1', name: 'ha-mcp' }];
      }
      // GET /api/v1/servers/srv-1 returns full server
      return {
        id: 'srv-1',
        name: 'ha-mcp',
        description: 'Old desc',
        transport: 'STDIO',
        replicas: 1,
        createdAt: '2025-01-01',
        updatedAt: '2025-01-01',
        version: 1,
      };
    });

    const cmd = createEditCommand({
      client,
      log,
      getEditor: () => 'vi',
      openEditor: (filePath) => {
        // Simulate user editing the file
        const content = readFileSync(filePath, 'utf-8');
        const modified = content
          .replace('Old desc', 'New desc')
          .replace('replicas: 1', 'replicas: 3');
        writeFileSync(filePath, modified, 'utf-8');
      },
    });

    await cmd.parseAsync(['server', 'ha-mcp'], { from: 'user' });

    expect(client.put).toHaveBeenCalledWith('/api/v1/servers/srv-1', expect.objectContaining({
      description: 'New desc',
      replicas: 3,
    }));
    expect(output.join('\n')).toContain("server 'ha-mcp' updated");
  });

  it('detects no changes and skips PUT', async () => {
    vi.mocked(client.get).mockImplementation(async (path: string) => {
      if (path === '/api/v1/servers') return [{ id: 'srv-1', name: 'test' }];
      return {
        id: 'srv-1', name: 'test', description: '', transport: 'STDIO',
        createdAt: '2025-01-01', updatedAt: '2025-01-01', version: 1,
      };
    });

    const cmd = createEditCommand({
      client,
      log,
      getEditor: () => 'vi',
      openEditor: () => {
        // Don't modify the file
      },
    });

    await cmd.parseAsync(['server', 'test'], { from: 'user' });

    expect(client.put).not.toHaveBeenCalled();
    expect(output.join('\n')).toContain("unchanged");
  });

  it('handles empty file as cancel', async () => {
    vi.mocked(client.get).mockImplementation(async (path: string) => {
      if (path === '/api/v1/servers') return [{ id: 'srv-1', name: 'test' }];
      return { id: 'srv-1', name: 'test', createdAt: '2025-01-01', updatedAt: '2025-01-01', version: 1 };
    });

    const cmd = createEditCommand({
      client,
      log,
      getEditor: () => 'vi',
      openEditor: (filePath) => {
        writeFileSync(filePath, '', 'utf-8');
      },
    });

    await cmd.parseAsync(['server', 'test'], { from: 'user' });

    expect(client.put).not.toHaveBeenCalled();
    expect(output.join('\n')).toContain('cancelled');
  });

  it('strips read-only fields from editor content', async () => {
    vi.mocked(client.get).mockImplementation(async (path: string) => {
      if (path === '/api/v1/servers') return [{ id: 'srv-1', name: 'test' }];
      return {
        id: 'srv-1', name: 'test', description: '', transport: 'STDIO',
        createdAt: '2025-01-01', updatedAt: '2025-01-01', version: 1,
      };
    });

    let editorContent = '';
    const cmd = createEditCommand({
      client,
      log,
      getEditor: () => 'vi',
      openEditor: (filePath) => {
        editorContent = readFileSync(filePath, 'utf-8');
      },
    });

    await cmd.parseAsync(['server', 'test'], { from: 'user' });

    // The editor content should NOT contain read-only fields
    expect(editorContent).not.toContain('id:');
    expect(editorContent).not.toContain('createdAt');
    expect(editorContent).not.toContain('updatedAt');
    expect(editorContent).not.toContain('version');
    // But should contain editable fields
    expect(editorContent).toContain('name:');
  });

  it('rejects edit instance with error message', async () => {
    const cmd = createEditCommand({ client, log });

    await cmd.parseAsync(['instance', 'inst-1'], { from: 'user' });

    expect(client.get).not.toHaveBeenCalled();
    expect(client.put).not.toHaveBeenCalled();
    expect(output.join('\n')).toContain('immutable');
  });

});
