'use strict';

/**
 * Fabius AI Provider Abstraction Layer
 * 
 * Provides a unified interface for all AI operations regardless of provider.
 * Default: Mistral. Configurable: OpenAI, Anthropic, Google.
 */

const MistralProvider = require('./mistral');
const OpenAIProvider = require('./openai');
const AnthropicProvider = require('./anthropic');
const GoogleProvider = require('./google');

const PROVIDERS = {
  mistral:   MistralProvider,
  openai:    OpenAIProvider,
  anthropic: AnthropicProvider,
  google:    GoogleProvider,
};

class AIProvider {
  /**
   * @param {object} config
   * @param {string} config.provider - Provider name: 'mistral', 'openai', 'anthropic', 'google'
   * @param {string} config.apiKey - API key for the provider
   * @param {object} config.model - Model names per task
   * @param {number} config.temperature - Generation temperature (default 0.1)
   * @param {number} config.maxTokens - Max output tokens (default 4096)
   * @param {number} config.timeout - Request timeout in ms (default 30000)
   * @param {string} config.fallbackProvider - Fallback provider on error
   */
  constructor(config) {
    this.config = Object.assign({
      provider: 'mistral',
      temperature: 0.1,
      maxTokens: 4096,
      timeout: 30000,
      model: {},
    }, config);

    const ProviderClass = PROVIDERS[this.config.provider];
    if (!ProviderClass) {
      throw new Error(`Unknown AI provider: ${this.config.provider}. Available: ${Object.keys(PROVIDERS).join(', ')}`);
    }

    this.provider = new ProviderClass(this.config);

    // Set up fallback if configured
    this.fallback = null;
    if (this.config.fallbackProvider && this.config.fallbackProvider !== this.config.provider) {
      const FallbackClass = PROVIDERS[this.config.fallbackProvider];
      if (FallbackClass && this.config.fallbackApiKey) {
        this.fallback = new FallbackClass({
          ...this.config,
          provider: this.config.fallbackProvider,
          apiKey: this.config.fallbackApiKey,
        });
      }
    }
  }

  /**
   * Send a chat completion request to the AI provider
   * @param {Array<{role: string, content: string}>} messages
   * @param {object} options - Override options for this call
   * @returns {Promise<string>} AI response text
   */
  async chat(messages, options = {}) {
    try {
      return await this.provider.chat(messages, options);
    } catch (err) {
      if (this.fallback) {
        console.warn(`[Fabius AI] Primary provider failed, falling back: ${err.message}`);
        return this.fallback.chat(messages, options);
      }
      throw err;
    }
  }

  /**
   * Classify a security advisory into a structured threat object
   * @param {string} advisoryText - Raw advisory text
   * @returns {Promise<object>} Structured threat classification
   */
  async classifyThreat(advisoryText) {
    const messages = [
      {
        role: 'system',
        content: CLASSIFY_SYSTEM_PROMPT,
      },
      {
        role: 'user',
        content: `Analyze this security advisory and classify the threat:\n\n${advisoryText}`,
      },
    ];

    const response = await this.chat(messages, {
      temperature: 0.05,
      maxTokens: 2048,
    });

    return this._parseJSON(response);
  }

  /**
   * Generate detection code for a classified threat
   * @param {object} threat - Classified threat object
   * @param {string} referenceCode - Existing scanner code as reference
   * @returns {Promise<string>} JavaScript detection function
   */
  async generateDetectionCode(threat, referenceCode = '') {
    const messages = [
      {
        role: 'system',
        content: DETECTION_SYSTEM_PROMPT,
      },
      {
        role: 'user',
        content: `Generate detection code for this threat:\n\n${JSON.stringify(threat, null, 2)}\n\nReference scanner code for style/format:\n\`\`\`javascript\n${referenceCode.slice(0, 3000)}\n\`\`\``,
      },
    ];

    const response = await this.chat(messages, {
      temperature: 0.1,
      maxTokens: 4096,
    });

    return this._extractCode(response);
  }

  /**
   * Generate an agent workflow markdown file for a threat
   * @param {object} threat - Classified threat object
   * @param {string} templateWorkflow - Existing workflow as template
   * @returns {Promise<string>} Markdown workflow content
   */
  async generateWorkflow(threat, templateWorkflow = '') {
    const messages = [
      {
        role: 'system',
        content: WORKFLOW_SYSTEM_PROMPT,
      },
      {
        role: 'user',
        content: `Generate an agent workflow for this threat:\n\n${JSON.stringify(threat, null, 2)}\n\nUse this existing workflow as a format reference:\n\`\`\`markdown\n${templateWorkflow.slice(0, 2000)}\n\`\`\``,
      },
    ];

    const response = await this.chat(messages, {
      temperature: 0.15,
      maxTokens: 3000,
    });

    return response.trim();
  }

  /**
   * Generate remediation instructions for a detected threat in a repository
   * @param {object} threat - Classified threat object
   * @param {object} context - Repository context (affected files, versions, etc.)
   * @returns {Promise<object>} Remediation plan with patches
   */
  async generateRemediation(threat, context) {
    const messages = [
      {
        role: 'system',
        content: REMEDIATION_SYSTEM_PROMPT,
      },
      {
        role: 'user',
        content: `Generate remediation for this threat in the given context:\n\nThreat:\n${JSON.stringify(threat, null, 2)}\n\nContext:\n${JSON.stringify(context, null, 2)}`,
      },
    ];

    const response = await this.chat(messages, {
      temperature: 0.1,
      maxTokens: 4096,
    });

    return this._parseJSON(response);
  }

  /**
   * Parse a JSON response from AI, handling markdown code blocks
   */
  _parseJSON(text) {
    // Strip markdown code blocks if present
    let clean = text.trim();
    const jsonMatch = clean.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
    if (jsonMatch) {
      clean = jsonMatch[1].trim();
    }

    try {
      return JSON.parse(clean);
    } catch (err) {
      // Try to find the first { ... } or [ ... ] block
      const objMatch = clean.match(/(\{[\s\S]*\})/);
      if (objMatch) {
        return JSON.parse(objMatch[1]);
      }
      throw new Error(`Failed to parse AI response as JSON: ${err.message}\nResponse: ${text.slice(0, 500)}`);
    }
  }

  /**
   * Extract code from a markdown-wrapped AI response
   */
  _extractCode(text) {
    const codeMatch = text.match(/```(?:javascript|js)?\s*\n?([\s\S]*?)\n?```/);
    if (codeMatch) {
      return codeMatch[1].trim();
    }
    return text.trim();
  }

  /**
   * Get the current provider name
   */
  get name() {
    return this.config.provider;
  }
}

// ─── System Prompts ──────────────────────────────────────────────────────────

const CLASSIFY_SYSTEM_PROMPT = `You are a cybersecurity threat analyst specializing in software supply chain attacks. 
Analyze security advisories and classify them into a structured JSON format.

You MUST respond with ONLY a JSON object (no markdown, no explanation) with this exact schema:
{
  "name": "Short descriptive name",
  "description": "2-3 sentence description of the attack",
  "severity": "critical|high|medium|low|info",
  "ecosystems": ["npm"],
  "affectedPackages": [
    { "name": "package-name", "versions": ["1.0.0", "1.0.1"], "type": "compromised|malicious|dependency" }
  ],
  "attackVector": "dependency-injection|typosquatting|account-takeover|malicious-code|backdoor",
  "indicators": ["list", "of", "IOCs"],
  "mitigations": ["list", "of", "recommended", "actions"],
  "references": ["urls"]
}

If the advisory is NOT about a supply chain attack or is not relevant to software development, respond with:
{ "relevant": false, "reason": "brief explanation" }`;

const DETECTION_SYSTEM_PROMPT = `You are a security engineer writing detection code for supply chain attacks.
Generate a self-contained JavaScript function that detects a specific threat.

Requirements:
- Pure Node.js (no dependencies)
- Function signature: function detect(filePath, content, context) => { found: boolean, severity: string, details: string }
- Use regex patterns and string analysis
- Check file content, package.json, lock files as appropriate
- Be specific to the threat — no generic checks
- Include comments explaining each check
- Return ONLY the JavaScript code in a code block`;

const WORKFLOW_SYSTEM_PROMPT = `You are a technical writer creating agent workflow files for AI coding assistants.
Generate a markdown workflow file that agents can follow to scan for and remediate a specific threat.

Format requirements:
- Start with YAML frontmatter: ---\ndescription: short description\n---
- Use numbered steps
- Add "// turbo" comment above steps that are safe to auto-run
- Include specific commands (PowerShell for Windows)
- Be precise and actionable
- End with "Report results to the user."`;

const REMEDIATION_SYSTEM_PROMPT = `You are a security engineer generating remediation plans for supply chain attacks.
Generate a structured JSON remediation plan.

You MUST respond with ONLY a JSON object with this schema:
{
  "summary": "1-2 sentence summary of what to fix",
  "steps": [
    {
      "description": "What this step does",
      "type": "package-pin|package-remove|file-patch|lockfile-regen|hook-install|node-modules-clean",
      "file": "relative/path/to/file",
      "before": "content to replace (if file-patch)",
      "after": "replacement content (if file-patch)",
      "command": "shell command to run (if applicable)"
    }
  ],
  "warnings": ["things the user should be aware of"],
  "rollbackSteps": ["how to undo if needed"]
}`;

module.exports = AIProvider;
