import { describe, it, expect } from 'vitest';
import {
  mergeProviderConfigPatch,
  ProviderConfigPatchSchema,
  CoderProvidersFileSchema,
  type CoderProvidersFile,
} from '../provider-config.js';

describe('ProviderConfigPatchSchema', () => {
  it('accepts a per-provider override patch', () => {
    const parsed = ProviderConfigPatchSchema.safeParse({ providers: { goose: { enabled: false } } });
    expect(parsed.success).toBe(true);
  });

  it('accepts a null value (delete-the-override sentinel)', () => {
    const parsed = ProviderConfigPatchSchema.safeParse({ providers: { goose: null } });
    expect(parsed.success).toBe(true);
  });

  it('defaults providers to {} on an empty body', () => {
    const parsed = ProviderConfigPatchSchema.safeParse({});
    expect(parsed.success).toBe(true);
    if (parsed.success) expect(parsed.data.providers).toEqual({});
  });

  it('rejects a malformed override (wrong field type)', () => {
    const parsed = ProviderConfigPatchSchema.safeParse({ providers: { goose: { enabled: 'yes' } } });
    expect(parsed.success).toBe(false);
  });

  it('rejects a non-object providers map', () => {
    const parsed = ProviderConfigPatchSchema.safeParse({ providers: 123 });
    expect(parsed.success).toBe(false);
  });
});

describe('mergeProviderConfigPatch', () => {
  const current: CoderProvidersFile = {
    providers: {
      goose: { enabled: true, label: 'Goose' },
      opencode: { enabled: true },
    },
  };

  it('replaces an existing override object wholesale (not deep-merge)', () => {
    const merged = mergeProviderConfigPatch(current, { providers: { goose: { enabled: false } } });
    // Whole override replaced — the prior `label` is gone, only `enabled` remains.
    expect(merged.providers.goose).toEqual({ enabled: false });
  });

  it('adds a brand-new override id', () => {
    const merged = mergeProviderConfigPatch(current, {
      providers: { 'amp-acp': { extends: 'acp', label: 'Amp', command: ['amp-acp'] } },
    });
    expect(merged.providers['amp-acp']).toEqual({ extends: 'acp', label: 'Amp', command: ['amp-acp'] });
  });

  it('deletes an override when the value is null', () => {
    const merged = mergeProviderConfigPatch(current, { providers: { goose: null } });
    expect(merged.providers.goose).toBeUndefined();
    expect(Object.keys(merged.providers)).toEqual(['opencode']);
  });

  it('leaves ids absent from the patch untouched', () => {
    const merged = mergeProviderConfigPatch(current, { providers: { goose: { enabled: false } } });
    expect(merged.providers.opencode).toEqual({ enabled: true });
  });

  it('does not mutate the input config', () => {
    const snapshot = JSON.parse(JSON.stringify(current));
    mergeProviderConfigPatch(current, { providers: { goose: null, opencode: { enabled: false } } });
    expect(current).toEqual(snapshot);
  });

  it('empty patch returns an equivalent config', () => {
    const merged = mergeProviderConfigPatch(current, { providers: {} });
    expect(merged).toEqual(current);
  });
});

describe('CoderProvidersFileSchema (validate-before-save guard)', () => {
  it('accepts a clean merged config', () => {
    const merged = mergeProviderConfigPatch(
      { providers: {} },
      { providers: { goose: { enabled: false } } },
    );
    expect(CoderProvidersFileSchema.safeParse(merged).success).toBe(true);
  });

  it('rejects a config carrying an invalid override (never written)', () => {
    // A merged object that somehow holds a bad override must fail validation
    // so the PATCH route returns 422 and never calls save().
    const invalid = { providers: { goose: { enabled: 'nope' } } };
    expect(CoderProvidersFileSchema.safeParse(invalid).success).toBe(false);
  });
});
