import { describe, it, expect } from 'vitest';
import { validateProxyModel } from '../src/proxymodel/schema.js';

describe('validateProxyModel', () => {
  const validModel = {
    kind: 'ProxyModel',
    metadata: { name: 'test' },
    spec: {
      controller: 'gate',
      stages: [{ type: 'passthrough' }],
      appliesTo: ['toolResult'],
      cacheable: true,
    },
  };

  it('validates a fully-specified model', () => {
    const result = validateProxyModel(validModel);
    expect(result.kind).toBe('ProxyModel');
    expect(result.metadata.name).toBe('test');
    expect(result.spec.controller).toBe('gate');
    expect(result.spec.stages).toHaveLength(1);
    expect(result.spec.stages[0]!.type).toBe('passthrough');
    expect(result.spec.appliesTo).toEqual(['toolResult']);
    expect(result.spec.cacheable).toBe(true);
    expect(result.source).toBe('local');
  });

  it('sets source to built-in when specified', () => {
    const result = validateProxyModel(validModel, 'built-in');
    expect(result.source).toBe('built-in');
  });

  it('defaults controller to gate', () => {
    const model = {
      metadata: { name: 'test' },
      spec: { stages: [{ type: 'passthrough' }] },
    };
    const result = validateProxyModel(model);
    expect(result.spec.controller).toBe('gate');
  });

  it('defaults appliesTo to prompt+toolResult', () => {
    const model = {
      metadata: { name: 'test' },
      spec: { stages: [{ type: 'passthrough' }] },
    };
    const result = validateProxyModel(model);
    expect(result.spec.appliesTo).toEqual(['prompt', 'toolResult']);
  });

  it('defaults cacheable to true', () => {
    const model = {
      metadata: { name: 'test' },
      spec: { stages: [{ type: 'passthrough' }] },
    };
    const result = validateProxyModel(model);
    expect(result.spec.cacheable).toBe(true);
  });

  it('accepts stage config objects', () => {
    const model = {
      metadata: { name: 'test' },
      spec: {
        stages: [
          { type: 'paginate', config: { pageSize: 4000 } },
          { type: 'section-split', config: { minSectionSize: 1000 } },
        ],
      },
    };
    const result = validateProxyModel(model);
    expect(result.spec.stages[0]!.config).toEqual({ pageSize: 4000 });
    expect(result.spec.stages[1]!.config).toEqual({ minSectionSize: 1000 });
  });

  it('omits controllerConfig when not provided', () => {
    const model = {
      metadata: { name: 'test' },
      spec: { stages: [{ type: 'passthrough' }] },
    };
    const result = validateProxyModel(model);
    expect(result.spec.controllerConfig).toBeUndefined();
  });

  it('accepts controllerConfig', () => {
    const model = {
      metadata: { name: 'test' },
      spec: {
        controller: 'gate',
        controllerConfig: { byteBudget: 8192 },
        stages: [{ type: 'passthrough' }],
      },
    };
    const result = validateProxyModel(model);
    expect(result.spec.controllerConfig).toEqual({ byteBudget: 8192 });
  });

  it('rejects non-object input', () => {
    expect(() => validateProxyModel(null)).toThrow('must be an object');
    expect(() => validateProxyModel('string')).toThrow('must be an object');
  });

  it('rejects missing metadata.name', () => {
    expect(() => validateProxyModel({ spec: { stages: [{ type: 'a' }] } })).toThrow('metadata.name');
    expect(() => validateProxyModel({ metadata: {}, spec: { stages: [{ type: 'a' }] } })).toThrow('metadata.name');
  });

  it('rejects missing spec', () => {
    expect(() => validateProxyModel({ metadata: { name: 'test' } })).toThrow('spec object');
  });

  it('rejects empty stages array', () => {
    expect(() => validateProxyModel({
      metadata: { name: 'test' },
      spec: { stages: [] },
    })).toThrow('non-empty array');
  });

  it('rejects stage without type', () => {
    expect(() => validateProxyModel({
      metadata: { name: 'test' },
      spec: { stages: [{ config: {} }] },
    })).toThrow('stages[0].type');
  });

  it('rejects invalid appliesTo values', () => {
    expect(() => validateProxyModel({
      metadata: { name: 'test' },
      spec: { stages: [{ type: 'a' }], appliesTo: ['invalid'] },
    })).toThrow("Invalid appliesTo value 'invalid'");
  });

  it('rejects wrong kind', () => {
    expect(() => validateProxyModel({
      kind: 'Other',
      metadata: { name: 'test' },
      spec: { stages: [{ type: 'a' }] },
    })).toThrow('Invalid kind');
  });

  it('accepts missing kind (optional)', () => {
    const result = validateProxyModel({
      metadata: { name: 'test' },
      spec: { stages: [{ type: 'passthrough' }] },
    });
    expect(result.kind).toBe('ProxyModel');
  });
});
