import type { TPermissionMode } from '@robota-sdk/agent-core';
import { describe, it, expect } from 'vitest';

import { buildSystemPrompt } from '../context/system-prompt-builder.js';
import {
  createPersonaSection,
  createSelfVerificationSection,
} from '../context/system-prompt-section-providers.js';

import type { ISystemPromptParams } from '../context/system-prompt-builder.js';

const BASE_PARAMS: ISystemPromptParams = {
  agentsMd: '',
  claudeMd: '',
  toolDescriptions: [],
  permissionMode: 'default',
  projectInfo: {
    type: 'node',
    name: 'my-project',
    language: 'typescript',
    packageManager: 'pnpm',
  },
};

describe('buildSystemPrompt', () => {
  it('returns a non-empty string', () => {
    const result = buildSystemPrompt(BASE_PARAMS);
    expect(typeof result).toBe('string');
    expect(result.length).toBeGreaterThan(0);
  });

  it('does not inject framework role instructions from composer code', () => {
    const result = buildSystemPrompt(BASE_PARAMS);
    expect(result).not.toContain('## Role');
    expect(result).not.toContain('You are an AI coding assistant');
    expect(result).not.toContain('Always be precise');
  });

  it('does not inject provider behavior instructions from composer code', () => {
    const result = buildSystemPrompt(BASE_PARAMS);
    expect(result).not.toContain('## Web Search');
    expect(result).not.toContain('MUST use the web_search tool');
  });

  it('includes AGENTS.md content when provided', () => {
    const result = buildSystemPrompt({ ...BASE_PARAMS, agentsMd: '## Custom Rules\nNo foo.' });
    expect(result).toContain('## Custom Rules');
    expect(result).toContain('No foo.');
  });

  it('includes CLAUDE.md content when provided', () => {
    const result = buildSystemPrompt({ ...BASE_PARAMS, claudeMd: '## Project Notes\nUse pnpm.' });
    expect(result).toContain('## Project Notes');
    expect(result).toContain('Use pnpm.');
  });

  it('includes project memory when provided without adding memory behavior instructions', () => {
    const result = buildSystemPrompt({
      ...BASE_PARAMS,
      memoryMd: '- Use pnpm for scripts.',
    });

    expect(result).toContain('## Project Memory');
    expect(result).toContain('- Use pnpm for scripts.');
    expect(result).not.toContain('automatically save');
  });

  it('includes tool descriptions', () => {
    const result = buildSystemPrompt({
      ...BASE_PARAMS,
      toolDescriptions: ['Bash: execute shell commands', 'Read: read file contents'],
    });
    expect(result).toContain('Bash: execute shell commands');
    expect(result).toContain('Read: read file contents');
  });

  it('interpolates every TPermissionMode value verbatim (CLI-072 TC-02)', () => {
    const modes: TPermissionMode[] = ['plan', 'default', 'acceptEdits', 'bypassPermissions'];
    for (const mode of modes) {
      const prompt = buildSystemPrompt({ ...BASE_PARAMS, permissionMode: mode });
      expect(prompt).toContain(`- **Permission mode:** ${mode}`);
      expect(prompt).not.toContain('Trust level:');
    }
  });

  it('includes the active permission mode and no trust-level label (CLI-072)', () => {
    const resultPlan = buildSystemPrompt({ ...BASE_PARAMS, permissionMode: 'plan' });
    expect(resultPlan).toContain('- **Permission mode:** plan');
    expect(resultPlan).not.toContain('Trust level:');

    const resultAccept = buildSystemPrompt({ ...BASE_PARAMS, permissionMode: 'acceptEdits' });
    expect(resultAccept).toContain('- **Permission mode:** acceptEdits');
  });

  it('includes response language as metadata when provided', () => {
    const result = buildSystemPrompt({ ...BASE_PARAMS, language: 'ko' });
    expect(result).toContain('## Response Language');
    expect(result).toContain('ko');
    expect(result).not.toContain('Always respond in ko');
  });

  it('includes project name and type in output', () => {
    const result = buildSystemPrompt(BASE_PARAMS);
    expect(result).toContain('my-project');
    expect(result).toMatch(/node|typescript/i);
  });

  it('includes package manager when present', () => {
    const result = buildSystemPrompt(BASE_PARAMS);
    expect(result).toContain('pnpm');
  });

  it('handles unknown project type gracefully', () => {
    const result = buildSystemPrompt({
      ...BASE_PARAMS,
      projectInfo: { type: 'unknown', language: 'unknown' },
    });
    expect(typeof result).toBe('string');
    expect(result.length).toBeGreaterThan(0);
  });

  it('does not include AGENTS.md section header when agentsMd is empty', () => {
    const result = buildSystemPrompt({ ...BASE_PARAMS, agentsMd: '' });
    // Should not have a prominent empty section
    expect(result).not.toMatch(/## Agent Instructions\s*\n\s*\n/);
  });

  it('AGENTS.md appears before CLAUDE.md in the prompt', () => {
    const result = buildSystemPrompt({
      ...BASE_PARAMS,
      agentsMd: 'AGENTS_MARKER',
      claudeMd: 'CLAUDE_MARKER',
    });
    const agentsIdx = result.indexOf('AGENTS_MARKER');
    const claudeIdx = result.indexOf('CLAUDE_MARKER');
    expect(agentsIdx).toBeGreaterThanOrEqual(0);
    expect(claudeIdx).toBeGreaterThanOrEqual(0);
    expect(agentsIdx).toBeLessThan(claudeIdx);
  });

  it('starts with project instruction content when AGENTS.md is provided', () => {
    const result = buildSystemPrompt({
      ...BASE_PARAMS,
      agentsMd: 'AGENTS_MARKER',
      claudeMd: 'CLAUDE_MARKER',
    });

    expect(result.startsWith('## Agent Instructions\nAGENTS_MARKER')).toBe(true);
  });

  describe('PRESET-003 persona section', () => {
    it('TC-01: createPersonaSection returns a section with source "persona"', () => {
      const section = createPersonaSection('x');
      expect(section.source).toBe('persona');
      expect(section.content).toBe('x');
    });

    it('TC-02: persona section priority is 5 and sorts before AGENTS.md band (5 < 10)', () => {
      const section = createPersonaSection('x');
      expect(section.priority).toBe(5);
      expect(section.priority).toBeLessThan(10);
    });

    it('TC-03/TC-04: composed prompt contains persona text AND a runtime base marker', () => {
      const result = buildSystemPrompt({
        ...BASE_PARAMS,
        persona: 'PERSONA_MARK',
        cwd: '/tmp/runtime-cwd-marker',
        toolDescriptions: ['Bash: execute shell commands'],
      });
      expect(result).toContain('PERSONA_MARK');
      // runtime base markers: working directory + tool description
      expect(result).toContain('/tmp/runtime-cwd-marker');
      expect(result).toContain('Bash: execute shell commands');
    });

    it('TC-05: persona text comes before the project-instruction / runtime markers', () => {
      const result = buildSystemPrompt({
        ...BASE_PARAMS,
        persona: 'PERSONA_MARK',
        agentsMd: 'AGENTS_MARKER',
        cwd: '/tmp/runtime-cwd-marker',
      });
      const personaIdx = result.indexOf('PERSONA_MARK');
      const agentsIdx = result.indexOf('AGENTS_MARKER');
      const cwdIdx = result.indexOf('/tmp/runtime-cwd-marker');
      expect(personaIdx).toBeGreaterThanOrEqual(0);
      // priority 5 < 10 (AGENTS.md) < 30 (cwd) — ordering is a priority-sort result, not hardcoded
      expect(personaIdx).toBeLessThan(agentsIdx);
      expect(personaIdx).toBeLessThan(cwdIdx);
    });

    it('TC-06: no persona (default) produces a prompt identical to one with no persona field', () => {
      const withoutField = buildSystemPrompt({ ...BASE_PARAMS });
      const withUndefined = buildSystemPrompt({ ...BASE_PARAMS, persona: undefined });
      const withEmpty = buildSystemPrompt({ ...BASE_PARAMS, persona: '' });
      const withBlank = buildSystemPrompt({ ...BASE_PARAMS, persona: '   ' });
      expect(withUndefined).toBe(withoutField);
      expect(withEmpty).toBe(withoutField);
      expect(withBlank).toBe(withoutField);
      expect(withoutField).not.toContain('PERSONA_MARK');
    });
  });

  describe('PRESET-017 self-verification section', () => {
    it('TC-01: selfVerification:true injects a verify-before-done directive into the prompt', () => {
      const result = buildSystemPrompt({ ...BASE_PARAMS, selfVerification: true });
      expect(result).toMatch(/verify/i);
      expect(result).toMatch(/tool results?/i);
    });

    it('TC-01: selfVerification false/omitted produces no self-verification section', () => {
      const omitted = buildSystemPrompt({ ...BASE_PARAMS });
      const disabled = buildSystemPrompt({ ...BASE_PARAMS, selfVerification: false });
      const sectionContent = createSelfVerificationSection().content;
      expect(omitted).not.toContain(sectionContent);
      expect(disabled).not.toContain(sectionContent);
      expect(disabled).toBe(omitted);
    });

    it('TC-02: createSelfVerificationSection has source "self-verification", priority 6, non-empty content', () => {
      const section = createSelfVerificationSection();
      expect(section.source).toBe('self-verification');
      expect(section.priority).toBe(6);
      expect(section.content.trim().length).toBeGreaterThan(0);
    });

    it('TC-02: section priority 6 sorts after persona (5) and before AGENTS.md (10)', () => {
      const section = createSelfVerificationSection();
      expect(section.priority).toBeGreaterThan(createPersonaSection('x').priority);
      expect(section.priority).toBeLessThan(10);
    });

    it('TC-06: section content avoids heavy emphasis cues and contains no Hangul', () => {
      const { content } = createSelfVerificationSection();
      expect(content).not.toMatch(/CRITICAL|MUST|show your reasoning/i);
      expect(content).not.toMatch(/\p{Script=Hangul}/u);
    });
  });

  describe('System prompt skill injection', () => {
    it('should include skill list in system prompt', () => {
      const result = buildSystemPrompt({
        ...BASE_PARAMS,
        skills: [
          { name: 'my-skill', description: 'Does useful things' },
          { name: 'hidden', description: 'Secret', disableModelInvocation: true },
        ],
      });

      expect(result).toContain('## Skills');
      expect(result).toContain('my-skill: Does useful things');
      expect(result).not.toContain('Skills are metadata only until activated.');
      expect(result).not.toContain('hidden');
    });

    it('should not include skills section when no skills provided', () => {
      const result = buildSystemPrompt({ ...BASE_PARAMS });
      expect(result).not.toContain('## Skills');
    });

    it('should not include skills section when skills array is empty', () => {
      const result = buildSystemPrompt({ ...BASE_PARAMS, skills: [] });
      expect(result).not.toContain('## Skills');
    });

    it('should not include skills section when all skills are model-invocation disabled', () => {
      const result = buildSystemPrompt({
        ...BASE_PARAMS,
        skills: [{ name: 'hidden', description: 'Secret', disableModelInvocation: true }],
      });
      expect(result).not.toContain('## Skills');
    });

    it('should include multiple invocable skills', () => {
      const result = buildSystemPrompt({
        ...BASE_PARAMS,
        skills: [
          { name: 'skill-a', description: 'First skill' },
          { name: 'skill-b', description: 'Second skill' },
        ],
      });

      expect(result).toContain('- skill-a: First skill');
      expect(result).toContain('- skill-b: Second skill');
    });

    it('should place skills section after tools section', () => {
      const result = buildSystemPrompt({
        ...BASE_PARAMS,
        toolDescriptions: ['Bash: execute commands'],
        skills: [{ name: 'my-skill', description: 'Does things' }],
      });

      const toolsIdx = result.indexOf('## Available Tools');
      const skillsIdx = result.indexOf('## Skills');
      expect(toolsIdx).toBeGreaterThanOrEqual(0);
      expect(skillsIdx).toBeGreaterThan(toolsIdx);
    });

    it('separates built-in commands from skills and agents with structural headings', () => {
      const result = buildSystemPrompt({
        ...BASE_PARAMS,
        commandDescriptors: [
          {
            name: 'agent',
            kind: 'builtin-command',
            description: 'Subagent job command',
            userInvocable: true,
            modelInvocable: true,
            argumentHint: 'parallel LABEL:"PROMPT"',
            safety: 'background-agent',
          },
        ],
        skills: [{ name: 'review', description: 'Review code' }],
        agents: [{ name: 'Plan', description: 'Planning agent' }],
      });

      const commandsIdx = result.indexOf('## Built-in Commands');
      const skillsIdx = result.indexOf('## Skills');
      const agentsIdx = result.indexOf('## Agents');

      expect(commandsIdx).toBeGreaterThanOrEqual(0);
      expect(skillsIdx).toBeGreaterThan(commandsIdx);
      expect(agentsIdx).toBeGreaterThan(skillsIdx);
      expect(result).not.toContain('## Capabilities');
      expect(result).toContain('- agent parallel LABEL:"PROMPT": Subagent job command');
      expect(result).toContain('- review: Review code');
      expect(result).toContain('- Plan: Planning agent');
    });

    it('renders model-visible memory command guidance from the descriptor only', () => {
      const result = buildSystemPrompt({
        ...BASE_PARAMS,
        commandDescriptors: [
          {
            name: 'memory',
            kind: 'builtin-command',
            description:
              'Project memory command. Use it to inspect project memory when stored context may help, save durable preferences, project conventions, feedback, or references worth reusing across sessions, review pending candidates, and report memory provenance. Do not store secrets, credentials, or transient facts.',
            userInvocable: true,
            modelInvocable: true,
            argumentHint:
              'list | show [topic] | add <user|feedback|project|reference> <topic> <text> | pending | approve <id> | reject <id> | used',
            safety: 'write',
          },
        ],
      });

      expect(result).toContain('## Built-in Commands');
      expect(result).toContain('memory list | show [topic]');
      expect(result).toContain('inspect project memory when stored context may help');
      expect(result).toContain('Do not store secrets');
      expect(result).not.toContain('## Memory Behavior');
      expect(result).not.toContain('always save memory');
    });
  });

  describe('System prompt agent injection', () => {
    it('should include available agents as capability descriptors', () => {
      const result = buildSystemPrompt({
        ...BASE_PARAMS,
        agents: [
          { name: 'general-purpose', description: 'General task execution' },
          { name: 'Explore', description: 'Read-only exploration' },
        ],
      });

      expect(result).toContain('## Agents');
      expect(result).toContain('- general-purpose: General task execution');
      expect(result).toContain('- Explore: Read-only exploration');
    });

    it('should not include agents section when no agents are provided', () => {
      const result = buildSystemPrompt({ ...BASE_PARAMS, agents: [] });
      expect(result).not.toContain('## Agents');
    });
  });
});
