import { describe, expect, it } from 'vitest';
import {
  validateExtraArgs,
  isManagedFlag,
  stripShadowingFlags,
} from '../inference/llama-args-validator.js';
import { parseAgentsMd } from '../agents.js';

describe('validateExtraArgs', () => {
  describe('deny list — each alias rejected', () => {
    const denied = [
      '-m', '--model',
      '-mu', '--model-url',
      '-dr', '--docker-repo',
      '-hf', '-hfr', '--hf-repo',
      '-hff', '--hf-file',
      '-hfv', '-hfrv', '--hf-repo-v',
      '-hffv', '--hf-file-v',
      '-hft', '--hf-token',
      '-mm', '--mmproj',
      '-mmu', '--mmproj-url',
      '--host', '--port', '--path', '--api-prefix', '--reuse-port',
      '--api-key', '--api-key-file',
      '--ssl-key-file', '--ssl-cert-file',
      '--webui', '--no-webui', '--ui', '--no-ui',
      '--ui-config', '--ui-config-file',
      '--ui-mcp-proxy', '--no-ui-mcp-proxy',
      '--models-dir', '--models-preset', '--models-max',
      '--models-autoload', '--no-models-autoload',
    ];
    for (const flag of denied) {
      it(`rejects ${flag}`, () => {
        expect(() => validateExtraArgs([flag])).toThrow(/managed/);
      });
    }
  });

  describe('safe flags accepted', () => {
    const safe = [
      '-c', '--ctx-size', '-ngl', '--gpu-layers',
      '--top-k', '--cache-type-k', '--jinja', '--no-jinja',
      '--spec-draft-n-max', '-fa', '--flash-attn',
      '-t', '--threads', '-np', '--parallel',
    ];
    for (const flag of safe) {
      it(`accepts ${flag}`, () => {
        expect(() => validateExtraArgs([flag])).not.toThrow();
        expect(validateExtraArgs([flag])).toEqual([flag]);
      });
    }
  });

  it('handles --flag=value shape (denies the flag part)', () => {
    expect(() => validateExtraArgs(['--model=evil.gguf'])).toThrow(/managed/);
  });

  it('handles --flag=value shape (accepts safe flag)', () => {
    expect(validateExtraArgs(['--ctx-size=4096'])).toEqual(['--ctx-size=4096']);
  });

  it('returns empty array for undefined input', () => {
    expect(validateExtraArgs(undefined)).toEqual([]);
  });

  it('returns empty array for empty input', () => {
    expect(validateExtraArgs([])).toEqual([]);
  });

  it('treats negative numbers as values, not flags', () => {
    expect(validateExtraArgs(['--seed', '-1'])).toEqual(['--seed', '-1']);
  });
});

describe('isManagedFlag', () => {
  it('returns true for denied flags', () => {
    expect(isManagedFlag('--model')).toBe(true);
    expect(isManagedFlag('-m')).toBe(true);
    expect(isManagedFlag('--api-key')).toBe(true);
    expect(isManagedFlag('--port')).toBe(true);
  });

  it('returns false for safe flags', () => {
    expect(isManagedFlag('-c')).toBe(false);
    expect(isManagedFlag('--ctx-size')).toBe(false);
    expect(isManagedFlag('--top-k')).toBe(false);
  });
});

describe('stripShadowingFlags', () => {
  it('strips auto -c when user supplies -c', () => {
    const result = stripShadowingFlags(['-c', '4096', '--top-k', '40']);
    expect(result).toEqual(['--top-k', '40']);
  });

  it('retains both when no overlap', () => {
    const result = stripShadowingFlags(['--top-k', '40', '--top-p', '0.95']);
    expect(result).toEqual(['--top-k', '40', '--top-p', '0.95']);
  });

  it('strips --ctx-size=value form', () => {
    const result = stripShadowingFlags(['--ctx-size=4096']);
    expect(result).toEqual([]);
  });

  it('strips boolean --jinja flag (no value consumed)', () => {
    const result = stripShadowingFlags(['--jinja', '--top-k', '40']);
    expect(result).toEqual(['--top-k', '40']);
  });

  it('respects stripContext=false to keep context flags', () => {
    const result = stripShadowingFlags(['-c', '4096'], { stripContext: false });
    expect(result).toEqual(['-c', '4096']);
  });

  it('strips cache flags by default', () => {
    const result = stripShadowingFlags(['--cache-type-k', 'q8_0']);
    expect(result).toEqual([]);
  });

  it('strips spec flags by default', () => {
    const result = stripShadowingFlags(['--spec-draft-n-max', '16']);
    expect(result).toEqual([]);
  });
});

describe('AGENTS.md frontmatter validation', () => {
  it('rejects agent with managed flag in llama_extra_args', () => {
    const md = `## Evil Agent
---
llama_extra_args: ["--model", "evil.gguf"]
---
You are evil.`;
    const { agents, errors } = parseAgentsMd(md);
    expect(agents).toHaveLength(0);
    expect(errors).toHaveLength(1);
    expect(errors[0]!.reason).toContain('managed');
  });

  it('accepts agent with safe llama_extra_args', () => {
    const md = `## Good Agent
---
llama_extra_args: ["--top-k", "20"]
---
You are good.`;
    const { agents, errors } = parseAgentsMd(md);
    expect(errors).toHaveLength(0);
    expect(agents).toHaveLength(1);
    expect(agents[0]!.llama_extra_args).toEqual(['--top-k', '20']);
  });

  it('agent without llama_extra_args has null field', () => {
    const md = `## Simple Agent
---
temperature: 0.5
---
You are simple.`;
    const { agents } = parseAgentsMd(md);
    expect(agents[0]!.llama_extra_args).toBeNull();
  });
});
