import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdtemp, writeFile, rm, mkdir } from 'node:fs/promises';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { loadProxyModels, getProxyModel } from '../src/proxymodel/loader.js';

describe('loadProxyModels', () => {
  let tempDir: string;

  beforeEach(async () => {
    tempDir = await mkdtemp(join(tmpdir(), 'proxymodel-test-'));
  });

  afterEach(async () => {
    await rm(tempDir, { recursive: true, force: true });
  });

  it('loads built-in models when directory is empty', async () => {
    const models = await loadProxyModels(tempDir);
    expect(models.has('default')).toBe(true);
    expect(models.has('subindex')).toBe(true);
    expect(models.get('default')!.source).toBe('built-in');
  });

  it('loads built-in models when directory does not exist', async () => {
    const models = await loadProxyModels(join(tempDir, 'nonexistent'));
    expect(models.has('default')).toBe(true);
    expect(models.has('subindex')).toBe(true);
  });

  it('loads local YAML files', async () => {
    const yaml = `kind: ProxyModel
metadata:
  name: custom
spec:
  stages:
    - type: passthrough
`;
    await writeFile(join(tempDir, 'custom.yaml'), yaml);
    const models = await loadProxyModels(tempDir);
    expect(models.has('custom')).toBe(true);
    expect(models.get('custom')!.source).toBe('local');
    expect(models.get('custom')!.spec.stages[0]!.type).toBe('passthrough');
  });

  it('loads .yml files too', async () => {
    const yaml = `metadata:
  name: alt
spec:
  stages:
    - type: paginate
      config:
        pageSize: 4000
`;
    await writeFile(join(tempDir, 'alt.yml'), yaml);
    const models = await loadProxyModels(tempDir);
    expect(models.has('alt')).toBe(true);
    expect(models.get('alt')!.spec.stages[0]!.config).toEqual({ pageSize: 4000 });
  });

  it('local models override built-ins with same name', async () => {
    const yaml = `kind: ProxyModel
metadata:
  name: default
spec:
  controller: none
  stages:
    - type: passthrough
  cacheable: false
`;
    await writeFile(join(tempDir, 'default.yaml'), yaml);
    const models = await loadProxyModels(tempDir);
    expect(models.get('default')!.source).toBe('local');
    expect(models.get('default')!.spec.controller).toBe('none');
  });

  it('skips invalid YAML files without breaking', async () => {
    await writeFile(join(tempDir, 'valid.yaml'), `metadata:\n  name: good\nspec:\n  stages:\n    - type: passthrough\n`);
    await writeFile(join(tempDir, 'invalid.yaml'), `metadata:\n  name: \nspec:\n  stages: []\n`);
    const models = await loadProxyModels(tempDir);
    expect(models.has('good')).toBe(true);
    expect(models.has('')).toBe(false);
  });

  it('ignores non-yaml files', async () => {
    await writeFile(join(tempDir, 'readme.md'), '# Readme');
    await writeFile(join(tempDir, 'notes.txt'), 'Notes');
    const models = await loadProxyModels(tempDir);
    // Only built-ins
    expect(models.size).toBe(2);
  });
});

describe('getProxyModel', () => {
  let tempDir: string;

  beforeEach(async () => {
    tempDir = await mkdtemp(join(tmpdir(), 'proxymodel-test-'));
  });

  afterEach(async () => {
    await rm(tempDir, { recursive: true, force: true });
  });

  it('returns requested model by name', async () => {
    const model = await getProxyModel('subindex', tempDir);
    expect(model.metadata.name).toBe('subindex');
  });

  it('falls back to default for unknown model', async () => {
    const model = await getProxyModel('nonexistent', tempDir);
    expect(model.metadata.name).toBe('default');
  });
});
