/**
 * E2E Tests for Skill Session Management
 *
 * Tests skill session and tool authorization:
 * - Tool call validation within skill context
 * - Multi-skill session support (tool allowlist union)
 * - Integration between skills and tools
 *
 * Note: Full session activation tests require session manager
 * to be configured in the app. These tests focus on the
 * observable behavior through the MCP protocol.
 */
import { test, expect } from '@frontmcp/testing';
import { searchSkills, loadSkills } from './helpers/skills-protocol';

interface SkillResult {
  id: string;
  name: string;
  description: string;
  instructions: string;
  tools: Array<{ name: string; purpose?: string; available: boolean }>;
  parameters?: Array<{ name: string; description?: string; required?: boolean; type?: string }>;
  availableTools: string[];
  missingTools: string[];
  isComplete: boolean;
  warning?: string;
  formattedContent: string;
  session?: {
    activated: boolean;
    sessionId?: string;
    policyMode?: 'strict' | 'approval' | 'permissive';
    allowedTools?: string[];
  };
}

interface LoadSkillsResult {
  skills: SkillResult[];
  summary: {
    totalSkills: number;
    totalTools: number;
    allToolsAvailable: boolean;
    combinedWarnings?: string[];
  };
  nextSteps: string;
}

interface SearchSkillsResult {
  skills: Array<{ id: string; name: string; description: string; score: number }>;
  total: number;
  hasMore: boolean;
}

interface GitHubPRResult {
  pr: {
    number: number;
    title: string;
    author: string;
    status: string;
    files: string[];
  };
}

interface CommentResult {
  commentId: number;
  success: boolean;
}

interface SlackResult {
  messageId: string;
  success: boolean;
  timestamp: string;
}

test.describe('Skill Session E2E', () => {
  test.use({
    server: 'apps/e2e/demo-e2e-skills/src/main.ts',
    project: 'demo-e2e-skills',
    publicMode: true,
  });

  test.describe('Tool Availability in Skills', () => {
    test('should correctly identify available tools when loading skill', async ({ mcp }) => {
      const result = await loadSkills(mcp, {
        skillIds: ['review-pr'],
      });

      expect(result).toBeDefined();

      const content = result as LoadSkillsResult;
      const skill = content.skills[0];
      // These tools are registered in the app
      expect(skill.availableTools).toContain('github_get_pr');
      expect(skill.availableTools).toContain('github_add_comment');
    });

    test('should correctly identify missing tools when loading skill', async ({ mcp }) => {
      const result = await loadSkills(mcp, {
        skillIds: ['deploy-app'],
      });

      expect(result).toBeDefined();

      const content = result as LoadSkillsResult;
      const skill = content.skills[0];
      // These tools are NOT registered in the app
      expect(skill.missingTools).toContain('docker_build');
      expect(skill.missingTools).toContain('docker_push');
      expect(skill.missingTools).toContain('k8s_apply');
      // slack_notify IS registered
      expect(skill.availableTools).toContain('slack_notify');
    });
  });

  test.describe('Tool Call Without Skill Context', () => {
    test('should allow tool calls without active skill', async ({ mcp }) => {
      // Without skill session enforcement, tools should work directly
      const result = await mcp.tools.call('github_get_pr', {
        prNumber: 123,
      });

      expect(result).toBeSuccessful();

      const content = result.json<GitHubPRResult>();
      expect(content.pr).toBeDefined();
      expect(content.pr.number).toBe(123);
    });

    test('should allow all registered tools without skill context', async ({ mcp }) => {
      // Test all three registered tools
      const ghResult = await mcp.tools.call('github_get_pr', { prNumber: 456 });
      expect(ghResult).toBeSuccessful();

      const commentResult = await mcp.tools.call('github_add_comment', {
        prNumber: 456,
        comment: 'Test comment',
      });
      expect(commentResult).toBeSuccessful();

      const slackResult = await mcp.tools.call('slack_notify', {
        channel: 'test-channel',
        message: 'Test notification',
      });
      expect(slackResult).toBeSuccessful();
    });
  });

  test.describe('Multi-Skill Tool Coverage', () => {
    test('should show combined tool availability across skills', async ({ mcp }) => {
      // Load skill that uses github tools
      const reviewResult = await loadSkills(mcp, {
        skillIds: ['review-pr'],
      });

      expect(reviewResult).toBeDefined();
      const reviewContent = reviewResult as LoadSkillsResult;

      // Load skill that uses slack tools
      const notifyResult = await loadSkills(mcp, {
        skillIds: ['notify-team'],
      });

      expect(notifyResult).toBeDefined();
      const notifyContent = notifyResult as LoadSkillsResult;

      // Review skill should have github tools
      expect(reviewContent.skills[0].availableTools).toContain('github_get_pr');
      expect(reviewContent.skills[0].availableTools).toContain('github_add_comment');

      // Notify skill should have slack tools
      expect(notifyContent.skills[0].availableTools).toContain('slack_notify');
    });

    test('should identify full workflow skill with multiple tool types', async ({ mcp }) => {
      const result = await loadSkills(mcp, {
        skillIds: ['full-pr-workflow'],
      });

      expect(result).toBeDefined();

      const content = result as LoadSkillsResult;
      const skill = content.skills[0];
      // This skill uses all three available tools
      expect(skill.availableTools).toContain('github_get_pr');
      expect(skill.availableTools).toContain('github_add_comment');
      expect(skill.availableTools).toContain('slack_notify');
      expect(skill.isComplete).toBe(true);
    });
  });

  test.describe('Skill Search and Load Workflow', () => {
    test('should find and load skill in typical workflow', async ({ mcp }) => {
      // Step 1: Search for relevant skills
      const searchResult = await searchSkills(mcp, {
        query: 'review pull request',
      });

      expect(searchResult).toBeDefined();

      const searchContent = searchResult as SearchSkillsResult;
      expect(searchContent.skills.length).toBeGreaterThan(0);

      // Step 2: Load the first matching skill
      const skillIdToLoad = searchContent.skills[0].id;
      const loadResult = await loadSkills(mcp, {
        skillIds: [skillIdToLoad],
      });

      expect(loadResult).toBeDefined();

      const loadContent = loadResult as LoadSkillsResult;
      const skill = loadContent.skills[0];
      expect(skill.instructions).toBeDefined();
      expect(skill.formattedContent).toBeDefined();

      // Step 3: Use a tool mentioned in the skill
      if (skill.availableTools.includes('github_get_pr')) {
        const toolResult = await mcp.tools.call('github_get_pr', {
          prNumber: 42,
        });

        expect(toolResult).toBeSuccessful();
      }
    });
  });

  test.describe('Tool Execution', () => {
    test('should execute github_get_pr tool correctly', async ({ mcp }) => {
      const result = await mcp.tools.call('github_get_pr', {
        prNumber: 789,
      });

      expect(result).toBeSuccessful();

      const content = result.json<GitHubPRResult>();
      expect(content.pr).toBeDefined();
      expect(content.pr.number).toBe(789);
      expect(content.pr.title).toBeDefined();
      expect(content.pr.author).toBeDefined();
      expect(content.pr.status).toBeDefined();
      expect(content.pr.files).toBeDefined();
    });

    test('should execute github_add_comment tool correctly', async ({ mcp }) => {
      const result = await mcp.tools.call('github_add_comment', {
        prNumber: 789,
        comment: 'This is a test review comment',
      });

      expect(result).toBeSuccessful();

      const content = result.json<CommentResult>();
      expect(content.commentId).toBeDefined();
      expect(content.success).toBe(true);
    });

    test('should execute slack_notify tool correctly', async ({ mcp }) => {
      const result = await mcp.tools.call('slack_notify', {
        channel: 'engineering',
        message: 'Deployment complete!',
      });

      expect(result).toBeSuccessful();

      const content = result.json<SlackResult>();
      expect(content.messageId).toBeDefined();
      expect(content.success).toBe(true);
      expect(content.timestamp).toBeDefined();
    });
  });

  test.describe('Skill Priority', () => {
    test('should respect skill priority in search results', async ({ mcp }) => {
      // deploy-app has priority: 10 (higher)
      const result = await searchSkills(mcp, {
        query: 'workflow process',
        limit: 10,
      });

      expect(result).toBeDefined();

      const content = result as SearchSkillsResult;
      const skillIds = content.skills.map((s) => s.id);

      // If deploy-app matches, it should appear due to priority boost
      if (skillIds.includes('deploy-app')) {
        // Priority should influence ranking
        expect(skillIds).toContain('deploy-app');
      }
    });
  });

  test.describe('Complete Workflow Integration', () => {
    test('should support full PR review workflow', async ({ mcp }) => {
      // 1. Find the review-pr skill
      const searchResult = await searchSkills(mcp, {
        query: 'PR review',
      });

      expect(searchResult).toBeDefined();

      // 2. Load the skill
      const loadResult = await loadSkills(mcp, {
        skillIds: ['review-pr'],
      });

      expect(loadResult).toBeDefined();

      // 3. Follow the skill instructions - get PR
      const prResult = await mcp.tools.call('github_get_pr', {
        prNumber: 100,
      });

      expect(prResult).toBeSuccessful();

      // 4. Add a review comment
      const commentResult = await mcp.tools.call('github_add_comment', {
        prNumber: 100,
        comment: 'LGTM! Nice refactoring work.',
      });

      expect(commentResult).toBeSuccessful();
    });

    test('should support notification workflow', async ({ mcp }) => {
      // 1. Load the notify-team skill
      const loadResult = await loadSkills(mcp, {
        skillIds: ['notify-team'],
      });

      expect(loadResult).toBeDefined();

      const loadContent = loadResult as LoadSkillsResult;
      expect(loadContent.skills[0].instructions).toContain('slack_notify');

      // 2. Send notification
      const notifyResult = await mcp.tools.call('slack_notify', {
        channel: 'engineering',
        message: 'PR #100 has been approved and merged!',
      });

      expect(notifyResult).toBeSuccessful();
    });
  });

  test.describe('Session Activation', () => {
    test('should return session info when activateSession is true and session context exists', async ({ mcp }) => {
      const result = await loadSkills(mcp, {
        skillIds: ['review-pr'],
        activateSession: true,
      });

      expect(result).toBeDefined();

      const content = result as LoadSkillsResult;
      const skill = content.skills[0];

      // Session object may or may not be present depending on session context availability
      // When present, it should have an activated field
      if (skill.session !== undefined) {
        expect(typeof skill.session.activated).toBe('boolean');
      }
    });

    test('should not return session info when activateSession is false', async ({ mcp }) => {
      const result = await loadSkills(mcp, {
        skillIds: ['review-pr'],
        activateSession: false,
      });

      expect(result).toBeDefined();

      const content = result as LoadSkillsResult;
      const skill = content.skills[0];

      // Session should not be present when activateSession is false
      expect(skill.session).toBeUndefined();
    });

    test('should not return session info when activateSession is not specified', async ({ mcp }) => {
      const result = await loadSkills(mcp, {
        skillIds: ['review-pr'],
      });

      expect(result).toBeDefined();

      const content = result as LoadSkillsResult;
      const skill = content.skills[0];

      // Session should not be present when activateSession defaults to false
      expect(skill.session).toBeUndefined();
    });
  });

  test.describe('Policy Mode Override', () => {
    test('should set strict policyMode when specified', async ({ mcp }) => {
      const result = await loadSkills(mcp, {
        skillIds: ['review-pr'],
        activateSession: true,
        policyMode: 'strict',
      });

      expect(result).toBeDefined();

      const content = result as LoadSkillsResult;
      const skill = content.skills[0];

      // If session was activated, verify policy mode
      if (skill.session?.activated) {
        expect(skill.session.policyMode).toBe('strict');
      }
    });

    test('should set approval policyMode when specified', async ({ mcp }) => {
      const result = await loadSkills(mcp, {
        skillIds: ['review-pr'],
        activateSession: true,
        policyMode: 'approval',
      });

      expect(result).toBeDefined();

      const content = result as LoadSkillsResult;
      const skill = content.skills[0];

      // If session was activated, verify policy mode
      if (skill.session?.activated) {
        expect(skill.session.policyMode).toBe('approval');
      }
    });

    test('should default to permissive policyMode', async ({ mcp }) => {
      const result = await loadSkills(mcp, {
        skillIds: ['review-pr'],
        activateSession: true,
      });

      expect(result).toBeDefined();

      const content = result as LoadSkillsResult;
      const skill = content.skills[0];

      // If session was activated, default policy mode should be permissive
      if (skill.session?.activated) {
        expect(skill.session.policyMode).toBe('permissive');
      }
    });
  });

  test.describe('Session Allowed Tools', () => {
    test('should include allowedTools in session when activated', async ({ mcp }) => {
      const result = await loadSkills(mcp, {
        skillIds: ['review-pr'],
        activateSession: true,
      });

      expect(result).toBeDefined();

      const content = result as LoadSkillsResult;
      const skill = content.skills[0];

      // If session was activated, allowedTools should match availableTools
      if (skill.session?.activated) {
        expect(skill.session.allowedTools).toBeDefined();
        expect(skill.session.allowedTools).toContain('github_get_pr');
        expect(skill.session.allowedTools).toContain('github_add_comment');
      }
    });

    test('should include full-pr-workflow tools in session', async ({ mcp }) => {
      const result = await loadSkills(mcp, {
        skillIds: ['full-pr-workflow'],
        activateSession: true,
      });

      expect(result).toBeDefined();

      const content = result as LoadSkillsResult;
      const skill = content.skills[0];

      // If session was activated, verify all tools from full workflow
      if (skill.session?.activated) {
        expect(skill.session.allowedTools).toContain('github_get_pr');
        expect(skill.session.allowedTools).toContain('github_add_comment');
        expect(skill.session.allowedTools).toContain('slack_notify');
      }
    });
  });
});
