/**
 * Get Components Guide MCP Tool
 *
 * This tool retrieves comprehensive integration guides for specific components
 * with intelligent path resolution and security validation.
 */

import { z } from 'zod';
import { config } from '../config.js';
import { GitLabError } from '../services/gitlabClient.js';

/**
 * Schema for get components guide tool parameters
 */
export const getComponentsGuideSchema = z.object({
  path: z.string().min(1).describe('Path to the component\'s llms.txt file (relative or absolute URL)')
});

export type GetComponentsGuideParams = z.infer<typeof getComponentsGuideSchema>;

/**
 * Validates if a path is safe and doesn't contain dangerous characters
 */
function isValidPath(path: string): boolean {
  // Check for empty path
  if (!path || path.trim().length === 0) {
    return false;
  }

  // Check for dangerous characters and patterns
  const dangerousPatterns = [
    /\.\./,           // Directory traversal
    /<script/i,       // Script injection
    /<\/script/i,     // Script injection
    /javascript:/i,   // JavaScript protocol
    /data:/i,         // Data protocol
    /vbscript:/i,     // VBScript protocol
    /[<>]/,           // HTML/XML tags
    /[\x00-\x1f]/,    // Control characters
  ];

  return !dangerousPatterns.some(pattern => pattern.test(path));
}

/**
 * Determines if a URL is external (not matching the internal GitLab host)
 */
function isExternalUrl(path: string, internalHost?: string): boolean {
  // If path doesn't start with http/https, it's a relative path (internal)
  if (!path.startsWith('http://') && !path.startsWith('https://')) {
    return false;
  }

  // If no internal host is configured, treat all absolute URLs as external
  if (!internalHost) {
    return true;
  }

  try {
    const url = new URL(path);
    return url.hostname !== internalHost;
  } catch (e) {
    // Malformed URL, treat as external to be safe
    return true;
  }
}



/**
 * Extracts the title from content (first markdown header or filename)
 */
function extractTitle(content: string, path: string): string {
  // Try to find the first markdown header
  const headerMatch = content.match(/^#\s+(.+)$/m);
  if (headerMatch) {
    return headerMatch[1].trim();
  }

  // Fall back to the filename or path
  const filename = path.split('/').pop() || path;
  return filename.replace(/\.(txt|md|markdown)$/i, '');
}

/**
 * Formats the guide content with metadata
 */
function formatGuideContent(content: string, path: string): string {
  const title = extractTitle(content, path);
  const contentLength = content.length;

  return `# Component Guide: ${title}

**Source Path:** \`${path}\`
**Content Length:** ${contentLength} characters
**Retrieved:** ${new Date().toISOString()}

---

${content}`;
}

/**
 * Fetches content from external URL using HTTP GET
 */
async function fetchExternalUrl(url: string): Promise<string> {
  try {
    console.log(`Fetching external URL: ${url}`);

    const response = await fetch(url, {
      headers: {
        'User-Agent': 'awesome-components-mcp/1.0.0'
      }
    });

    if (!response.ok) {
      throw new GitLabError(
        `Failed to fetch external URL (status: ${response.status})`,
        response.status,
        url
      );
    }

    const content = await response.text();
    console.log(`Successfully fetched ${content.length} characters from external URL`);

    return content;
  } catch (error) {
    if (error instanceof GitLabError) {
      throw error;
    }

    throw new GitLabError(
      `Network error while fetching external URL: ${error instanceof Error ? error.message : 'Unknown error'}`,
      undefined,
      url
    );
  }
}

/**
 * Interface for gitlab-mcp instruction
 */
interface GitLabMcpInstruction {
  action_type: 'mcp_call';
  tool_name: string;
  parameters: {
    project_id: string;
    file_path: string;
    ref: string;
  };
}

/**
 * Validates GitLab MCP configuration
 */
function validateGitLabMcpConfig(): { isValid: boolean; error?: string } {
  if (!config.internalGuidesProjectId) {
    return {
      isValid: false,
      error: 'GITLAB_PROJECT_ID is required for GitLab MCP instruction mode'
    };
  }

  return { isValid: true };
}

/**
 * Get Components Guide MCP Tool Implementation
 */
export async function getComponentsGuideTool(
  params: GetComponentsGuideParams
): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: boolean }> {
  try {
    const { path } = params;

    console.log(`Getting component guide for path: ${path}`);

    // Validate the path for security
    if (!isValidPath(path)) {
      const errorResponse = `# Error: Invalid Component Guide Path

The provided path contains invalid characters or format: \`${path}\`

**Security Note:** Paths cannot contain:
- Directory traversal patterns (\`../\`)
- Script injection attempts (\`<script>\`)
- Control characters or HTML tags
- Protocol handlers (javascript:, data:, etc.)

**Valid Examples:**
- \`yeepay/dynamicpassword/llms.txt\`
- \`common/authentication/guide.md\`
- \`https://external-gitlab.com/project/-/raw/main/component/llms.txt\`

**Suggestions:**
- Use relative paths from the repository root
- Use absolute GitLab raw file URLs for external repositories
- Ensure the path points to a valid documentation file`;

      return {
        content: [{
          type: 'text',
          text: errorResponse
        }],
        isError: true
      };
    }

    // Check if this is an external URL
    if (isExternalUrl(path, config.internalGitlabHost)) {
      console.log(`External URL detected: ${path}`);

      // For external URLs, use direct HTTP GET request
      const content = await fetchExternalUrl(path);
      const formattedContent = formatGuideContent(content, path);

      return {
        content: [{
          type: 'text',
          text: formattedContent
        }]
      };
    }

    // For relative paths and internal URLs, generate gitlab-mcp instruction
    console.log(`Relative/internal path detected, generating gitlab-mcp instruction: ${path}`);

    // Validate GitLab MCP configuration
    const configValidation = validateGitLabMcpConfig();
    if (!configValidation.isValid) {
      throw new Error(`GitLab MCP Configuration Error: ${configValidation.error}`);
    }

    // Derive file path for GitLab
    let filePathForGitlab = path;

      // If it's an internal URL, extract the path
      if (path.startsWith('http://') || path.startsWith('https://')) {
        try {
          const url = new URL(path);
          filePathForGitlab = url.pathname;
          if (filePathForGitlab.startsWith('/')) {
            filePathForGitlab = filePathForGitlab.substring(1);
          }
        } catch (e) {
          // If URL parsing fails, use the original path
        }
      }

      // Generate JSON instruction for LLM to call gitlab-mcp
      // project_id should be URL encoded only if it's a string (not a numeric ID)
      // and only if it's not already encoded
      let projectId = config.internalGuidesProjectId;
      if (typeof projectId === 'string' && isNaN(Number(projectId))) {
        // Check if it's already URL encoded by looking for % characters
        if (!projectId.includes('%')) {
          projectId = encodeURIComponent(projectId);
        }
      }

      const instruction: GitLabMcpInstruction = {
        action_type: 'mcp_call',
        tool_name: 'get_file_contents',
        parameters: {
          project_id: projectId!,
          file_path: filePathForGitlab,
          ref: 'main'
        }
      };

      const instructionOutput = `# Component Guide - GitLab MCP Instruction

To retrieve the component guide for \`${path}\`, please call the gitlab-mcp server with the following instruction:

\`\`\`json
${JSON.stringify(instruction, null, 2)}
\`\`\`

**Instructions for LLM:**
1. Use the above JSON instruction to call the gitlab-mcp server
2. Call the \`get_file_contents\` tool with the provided parameters
3. The response will contain the component guide content

**Configuration:**
- Project ID: \`${config.internalGuidesProjectId}\`
- File Path: \`${filePathForGitlab}\`
- Branch: \`main\`
- Original Path: \`${path}\``;

    return {
      content: [{
        type: 'text',
        text: instructionOutput
      }]
    };

  } catch (error) {
    console.error('Get components guide failed:', error);
    
    let errorMessage = 'Unknown error occurred';
    let statusInfo = '';
    let suggestions = `**Suggestions:**
- Check if the component path is correct
- Verify the file exists in the repository
- Ensure you have proper access permissions
- Try using the components_discovery tool to find available components`;
    
    if (error instanceof GitLabError) {
      errorMessage = `GitLab Error: ${error.message}`;
      if (error.statusCode) {
        statusInfo = `\n**Status Code:** ${error.statusCode}`;
        
        // Provide specific suggestions based on status code
        if (error.statusCode === 404) {
          suggestions = `**Suggestions:**
- Verify the component path is correct: \`${params.path}\`
- Check if the file exists in the repository
- Use the components_discovery tool to see available components
- Ensure the file extension is correct (e.g., llms.txt, guide.md)`;
        } else if (error.statusCode === 403) {
          suggestions = `**Suggestions:**
- This repository may be private and require authentication
- Check your GitLab Personal Access Token configuration
- Verify your token has read access to this repository
- Contact the repository administrator for access`;
        }
      }
      if (error.url) {
        statusInfo += `\n**URL:** ${error.url}`;
      }
    } else if (error instanceof Error) {
      errorMessage = `Error: ${error.message}`;
    } else {
      errorMessage = `Error: ${String(error)}`;
    }

    const errorTitle = error instanceof GitLabError && error.statusCode === 404 
      ? 'Component Guide Not Found' 
      : 'Failed to Retrieve Component Guide';

    const errorResponse = `# Error: ${errorTitle}

${errorMessage}${statusInfo}

${suggestions}

**Configuration:**
- GitLab Base URL: ${config.urls.gitlabBase}
- Authentication: ${config.auth.gitlabToken ? 'Configured' : 'Not configured'}
- Requested Path: \`${params.path}\``;

    return {
      content: [{
        type: 'text',
        text: errorResponse
      }],
      isError: true
    };
  }
}
