import { describe, it, expect } from 'vitest';
import { configSchema } from './schema.js';

describe('configSchema — sandbox', () => {
  const minimalConfig = {
    telegram: { bot_token: 'test', allowed_chat_ids: [1] },
    models: { default: { provider: 'anthropic', model: 'claude-3' } },
  };

  it('defaults sandbox to disabled', () => {
    const result = configSchema.parse(minimalConfig);
    expect(result.sandbox.enabled).toBe(false);
    expect(result.sandbox.image).toBe('node:22-slim');
    expect(result.sandbox.network).toBe('none');
    expect(result.sandbox.memory_limit).toBe('512m');
    expect(result.sandbox.cpu_limit).toBe('1.0');
    expect(result.sandbox.timeout_seconds).toBe(300);
  });

  it('accepts sandbox config', () => {
    const result = configSchema.parse({
      ...minimalConfig,
      sandbox: { enabled: true, image: 'ubuntu:24.04', network: 'bridge' },
    });
    expect(result.sandbox.enabled).toBe(true);
    expect(result.sandbox.image).toBe('ubuntu:24.04');
    expect(result.sandbox.network).toBe('bridge');
  });
});

describe('configSchema — agent_configs', () => {
  const minimalConfig = {
    telegram: { bot_token: 'test', allowed_chat_ids: [1] },
    models: { default: { provider: 'anthropic', model: 'claude-3' } },
  };

  it('defaults agent_configs to empty', () => {
    const result = configSchema.parse(minimalConfig);
    expect(result.agent_configs).toEqual({});
  });

  it('accepts named agent configs', () => {
    const result = configSchema.parse({
      ...minimalConfig,
      agent_configs: {
        assistant: {
          system_prompt: 'You are helpful.',
          model_tier: 'default',
          tool_profile: 'messaging',
        },
        coder: {
          model_tier: 'complex',
          tool_profile: 'coding',
          sandbox: true,
        },
      },
    });
    expect(result.agent_configs.assistant.system_prompt).toBe('You are helpful.');
    expect(result.agent_configs.assistant.tool_profile).toBe('messaging');
    expect(result.agent_configs.coder.sandbox).toBe(true);
  });
});

describe('configSchema — routing', () => {
  const minimalConfig = {
    telegram: { bot_token: 'test', allowed_chat_ids: [1] },
    models: { default: { provider: 'anthropic', model: 'claude-3' } },
  };

  it('defaults routing to empty', () => {
    const result = configSchema.parse(minimalConfig);
    expect(result.routing.default_agent).toBeUndefined();
    expect(result.routing.channels).toEqual({});
    expect(result.routing.senders).toEqual({});
  });

  it('accepts routing config', () => {
    const result = configSchema.parse({
      ...minimalConfig,
      routing: {
        default_agent: 'assistant',
        channels: { discord: 'coder' },
        senders: { 'telegram:12345': 'coder' },
      },
    });
    expect(result.routing.default_agent).toBe('assistant');
    expect(result.routing.channels.discord).toBe('coder');
    expect(result.routing.senders['telegram:12345']).toBe('coder');
  });
});

describe('configSchema — per-tier fallback', () => {
  const minimalConfig = {
    telegram: { bot_token: 'test', allowed_chat_ids: [1] },
    models: { default: { provider: 'anthropic', model: 'claude-3' } },
  };

  it('accepts per-tier fallback config', () => {
    const result = configSchema.parse({
      ...minimalConfig,
      models: {
        default: {
          provider: 'anthropic',
          model: 'claude-sonnet-4-5-20250929',
          fallback: { provider: 'github', model: 'claude-sonnet-4-5-20250929' },
        },
        fast: {
          provider: 'anthropic',
          model: 'claude-haiku-4-5-20251001',
          fallback: { provider: 'github', model: 'claude-haiku-4-5-20251001' },
        },
      },
    });
    expect(result.models.default.fallback?.provider).toBe('github');
    expect(result.models.fast?.fallback?.provider).toBe('github');
  });

  it('works without fallback field (backward compat)', () => {
    const result = configSchema.parse(minimalConfig);
    expect(result.models.default.fallback).toBeUndefined();
  });

  it('fallback does not itself accept a nested fallback', () => {
    const result = configSchema.parse({
      ...minimalConfig,
      models: {
        default: {
          provider: 'anthropic',
          model: 'claude-3',
          fallback: {
            provider: 'github',
            model: 'claude-3',
            fallback: { provider: 'ollama', model: 'llama' },
          },
        },
      },
    });
    // Zod strips unknown keys from the base schema, so nested fallback is dropped
    expect((result.models.default.fallback as Record<string, unknown>)?.fallback).toBeUndefined();
  });
});

describe('configSchema — models auth_mode', () => {
  const minimalConfig = {
    telegram: { bot_token: 'test', allowed_chat_ids: [1] },
    models: { default: { provider: 'anthropic', model: 'claude-3' } },
  };

  it('accepts auth_mode values per tier', () => {
    const result = configSchema.parse({
      ...minimalConfig,
      models: {
        default: { provider: 'openai', model: 'gpt-4o', auth_mode: 'api_key' },
        fast: { provider: 'openai', model: 'gpt-4o-mini', auth_mode: 'oauth' },
      },
    });

    expect(result.models.default.auth_mode).toBe('api_key');
    expect(result.models.fast?.auth_mode).toBe('oauth');
  });

  it('rejects invalid auth_mode values', () => {
    expect(() => {
      configSchema.parse({
        ...minimalConfig,
        models: {
          default: { provider: 'openai', model: 'gpt-4o', auth_mode: 'bogus' },
        },
      });
    }).toThrow(/auth_mode/i);
  });

  it('accepts vercel provider id', () => {
    const result = configSchema.parse({
      ...minimalConfig,
      models: {
        default: { provider: 'vercel', model: 'openai/gpt-4.1', endpoint: 'https://ai-gateway.vercel.sh/v1', api_key: 'test-key' },
      },
    });
    expect(result.models.default.provider).toBe('vercel');
  });
});

describe('configSchema — matrix', () => {
  const minimalConfig = {
    telegram: { bot_token: 'test', allowed_chat_ids: [1] },
    models: { default: { provider: 'anthropic', model: 'claude-3' } },
  };

  it('accepts matrix config and defaults sync_timeout_ms', () => {
    const result = configSchema.parse({
      ...minimalConfig,
      matrix: {
        homeserver_url: 'https://matrix.example.org',
        access_token: 'syt_test_token',
        allowed_room_ids: ['!room1:example.org'],
        require_mention: true,
      },
    });

    expect(result.matrix).toBeDefined();
    expect(result.matrix!.homeserver_url).toBe('https://matrix.example.org');
    expect(result.matrix!.access_token).toBe('syt_test_token');
    expect(result.matrix!.sync_timeout_ms).toBe(30000);
  });

  it('matrix config is optional', () => {
    const result = configSchema.parse(minimalConfig);
    expect(result.matrix).toBeUndefined();
  });

  it('rejects invalid homeserver_url', () => {
    expect(() => {
      configSchema.parse({
        ...minimalConfig,
        matrix: {
          homeserver_url: 'not-a-url',
          access_token: 'token',
        },
      });
    }).toThrow(/homeserver/i);
  });
});

describe('configSchema — skills watcher', () => {
  const minimalConfig = {
    telegram: { bot_token: 'test', allowed_chat_ids: [1] },
    models: { default: { provider: 'anthropic', model: 'claude-3' } },
  };

  it('defaults skills watcher settings', () => {
    const result = configSchema.parse(minimalConfig);
    expect(result.skills.load.watch).toBe(false);
    expect(result.skills.load.watch_debounce_ms).toBe(250);
    expect(result.skills.installation_execution).toBe('disabled');
    expect(result.skills.allow_shell_runner).toBe(false);
    expect(result.skills.shell_runner_allowlist).toEqual([]);
    expect(result.skills.shell_runner_governance.owner).toBeUndefined();
    expect(result.skills.shell_runner_governance.review_cadence_days).toBe(7);
    expect(result.skills.shell_runner_governance.promotion_min_success_rate).toBe(0.9);
  });

  it('accepts explicit installation execution policy', () => {
    const enabled = configSchema.parse({
      ...minimalConfig,
      skills: {
        installation_execution: 'enabled',
        allow_shell_runner: true,
        shell_runner_allowlist: ['npm install*'],
        shell_runner_governance: {
          owner: 'skills-team',
          review_cadence_days: 14,
          promotion_min_success_rate: 0.95,
        },
      },
    });

    expect(enabled.skills.installation_execution).toBe('enabled');
    expect(enabled.skills.allow_shell_runner).toBe(true);
    expect(enabled.skills.shell_runner_allowlist).toEqual(['npm install*']);
    expect(enabled.skills.shell_runner_governance.owner).toBe('skills-team');
    expect(enabled.skills.shell_runner_governance.review_cadence_days).toBe(14);
    expect(enabled.skills.shell_runner_governance.promotion_min_success_rate).toBe(0.95);
  });

  it('accepts explicit watcher settings', () => {
    const result = configSchema.parse({
      ...minimalConfig,
      skills: {
        load: {
          watch: true,
          watch_debounce_ms: 500,
        },
      },
    });

    expect(result.skills.load.watch).toBe(true);
    expect(result.skills.load.watch_debounce_ms).toBe(500);
  });
});

describe('configSchema automation', () => {
  const baseConfig = {
    telegram: { bot_token: 'test-token', allowed_chat_ids: [123] },
    models: { default: { provider: 'anthropic', model: 'claude-sonnet' } },
  };

  it('accepts config without automation section', () => {
    const result = configSchema.parse(baseConfig);
    expect(result.automation).toBeDefined();
    expect(result.automation.cron).toEqual([]);
  });

  it('accepts config with cron jobs', () => {
    const result = configSchema.parse({
      ...baseConfig,
      automation: {
        cron: [{
          name: 'morning-briefing',
          schedule: '0 9 * * *',
          message: 'Good morning!',
          output: { channel: 'telegram', peer: '123' },
        }],
      },
    });
    expect(result.automation.cron).toHaveLength(1);
    expect(result.automation.cron[0].name).toBe('morning-briefing');
    expect(result.automation.cron[0].enabled).toBe(true); // default
  });

  it('rejects cron job with empty name', () => {
    expect(() => configSchema.parse({
      ...baseConfig,
      automation: {
        cron: [{
          name: '',
          schedule: '0 9 * * *',
          message: 'test',
          output: { channel: 'telegram', peer: '123' },
        }],
      },
    })).toThrow();
  });

  it('rejects cron job with empty schedule', () => {
    expect(() => configSchema.parse({
      ...baseConfig,
      automation: {
        cron: [{
          name: 'test',
          schedule: '',
          message: 'test',
          output: { channel: 'telegram', peer: '123' },
        }],
      },
    })).toThrow();
  });

  it('accepts cron job with optional fields', () => {
    const result = configSchema.parse({
      ...baseConfig,
      automation: {
        cron: [{
          name: 'test',
          schedule: '0 9 * * *',
          message: 'test',
          output: { channel: 'telegram', peer: '123' },
          enabled: false,
          timezone: 'America/New_York',
        }],
      },
    });
    expect(result.automation.cron[0].enabled).toBe(false);
    expect(result.automation.cron[0].timezone).toBe('America/New_York');
  });
});

describe('configSchema — intents', () => {
  const minimalConfig = {
    telegram: { bot_token: 'test', allowed_chat_ids: [1] },
    models: { default: { provider: 'anthropic', model: 'claude-3' } },
  };

  it('defaults intents to disabled with no rules', () => {
    const result = configSchema.parse(minimalConfig);
    expect(result.intents.enabled).toBe(false);
    expect(result.intents.rules).toEqual([]);
  });

  it('accepts intent rule config', () => {
    const result = configSchema.parse({
      ...minimalConfig,
      intents: {
        enabled: true,
        match_threshold: 0.6,
        rules: [
          {
            name: 'deploy-rule',
            patterns: ['deploy *'],
            target: { type: 'agent', name: 'coder' },
            priority: 5,
            enabled: true,
          },
        ],
      },
    });

    expect(result.intents.enabled).toBe(true);
    expect(result.intents.rules[0].target.type).toBe('agent');
    expect(result.intents.rules[0].target.name).toBe('coder');
  });
});

describe('configSchema — routing_policy', () => {
  const minimalConfig = {
    telegram: { bot_token: 'test', allowed_chat_ids: [1] },
    models: { default: { provider: 'anthropic', model: 'claude-3' } },
  };

  it('defaults routing_policy values', () => {
    const result = configSchema.parse(minimalConfig);
    expect(result.routing_policy.enabled).toBe(false);
    expect(result.routing_policy.fast_path_threshold).toBe(0.85);
    expect(result.routing_policy.llm_threshold).toBe(0.5);
    expect(result.routing_policy.default_path).toBe('llm');
  });
});

describe('configSchema — history_index', () => {
  const minimalConfig = {
    telegram: { bot_token: 'test', allowed_chat_ids: [1] },
    models: { default: { provider: 'anthropic', model: 'claude-3' } },
  };

  it('defaults history indexing config', () => {
    const result = configSchema.parse(minimalConfig);
    expect(result.history_index.enabled).toBe(false);
    expect(result.history_index.max_keywords).toBe(8);
    expect(result.history_index.search_limit).toBe(10);
  });
});

describe('configSchema — memory injection strategy', () => {
  const minimalConfig = {
    telegram: { bot_token: 'test', allowed_chat_ids: [1] },
    models: { default: { provider: 'anthropic', model: 'claude-3' } },
  };

  it('defaults memory injection settings', () => {
    const result = configSchema.parse(minimalConfig);
    expect(result.memory.injection_strategy).toBe('all');
    expect(result.memory.max_injection_tokens).toBe(2000);
  });

  it('accepts adaptive memory injection settings', () => {
    const result = configSchema.parse({
      ...minimalConfig,
      memory: {
        injection_strategy: 'adaptive',
        max_injection_tokens: 1200,
      },
    });
    expect(result.memory.injection_strategy).toBe('adaptive');
    expect(result.memory.max_injection_tokens).toBe(1200);
  });
});

describe('configSchema — compaction importance threshold', () => {
  const minimalConfig = {
    telegram: { bot_token: 'test', allowed_chat_ids: [1] },
    models: { default: { provider: 'anthropic', model: 'claude-3' } },
  };

  it('defaults compaction importance threshold to disabled behavior', () => {
    const result = configSchema.parse(minimalConfig);
    expect(result.compaction.importance_threshold).toBe(1);
  });

  it('accepts a custom importance threshold', () => {
    const result = configSchema.parse({
      ...minimalConfig,
      compaction: {
        importance_threshold: 0.5,
      },
    });
    expect(result.compaction.importance_threshold).toBe(0.5);
  });
});

describe('configSchema — prompt context level', () => {
  const minimalConfig = {
    telegram: { bot_token: 'test', allowed_chat_ids: [1] },
    models: { default: { provider: 'anthropic', model: 'claude-3' } },
  };

  it('defaults prompt.context_level to normal', () => {
    const result = configSchema.parse(minimalConfig);
    expect(result.prompt.context_level).toBe('normal');
  });

  it('accepts valid context levels', () => {
    const result = configSchema.parse({
      ...minimalConfig,
      prompt: {
        context_level: 'debug',
      },
    });

    expect(result.prompt.context_level).toBe('debug');
  });

  it('rejects invalid context levels', () => {
    expect(() => configSchema.parse({
      ...minimalConfig,
      prompt: {
        context_level: 'verbose',
      },
    })).toThrow();
  });
});

describe('configSchema — agents truthfulness/autonomy', () => {
  const minimalConfig = {
    telegram: { bot_token: 'test', allowed_chat_ids: [1] },
    models: { default: { provider: 'anthropic', model: 'claude-3' } },
  };

  it('defaults to standard truthfulness and autonomy', () => {
    const result = configSchema.parse(minimalConfig);
    expect(result.agents.truthfulness_mode).toBe('standard');
    expect(result.agents.autonomy_level).toBe('standard');
  });

  it('accepts explicit truthfulness and autonomy modes', () => {
    const result = configSchema.parse({
      ...minimalConfig,
      agents: {
        truthfulness_mode: 'strict',
        autonomy_level: 'conservative',
      },
    });

    expect(result.agents.truthfulness_mode).toBe('strict');
    expect(result.agents.autonomy_level).toBe('conservative');
  });

  it('rejects invalid truthfulness_mode', () => {
    expect(() => configSchema.parse({
      ...minimalConfig,
      agents: {
        truthfulness_mode: 'always',
      },
    })).toThrow();
  });

  it('rejects invalid autonomy_level', () => {
    expect(() => configSchema.parse({
      ...minimalConfig,
      agents: {
        autonomy_level: 'manual',
      },
    })).toThrow();
  });
});
