/**
 * IterFact Assembly Planner — API Integration
 * 
 * This module calls the Anthropic API with the system prompt
 * and returns a validated Assembly Plan JSON.
 * 
 * Usage:
 *   const plan = await generateAssemblyPlan("Create a quarterly earnings report...");
 *   // plan is a validated JSON object ready for the Assembler
 */

// ═══════════════════════════════════════════════════════════
// CONFIGURATION
// ═══════════════════════════════════════════════════════════

const CONFIG = {
  model: "claude-sonnet-4-20250514",
  max_tokens: 4096,
  temperature: 0.3,     // Low temp = deterministic planning
  api_url: "https://api.anthropic.com/v1/messages",
};

// ═══════════════════════════════════════════════════════════
// SYSTEM PROMPT (loaded from system_prompt_raw.txt)
// In production, load from file. Inlined here for reference.
// ═══════════════════════════════════════════════════════════

const SYSTEM_PROMPT = `... contents of system_prompt_raw.txt ...`;
// In production: const SYSTEM_PROMPT = await fetch('/spec/system_prompt_raw.txt').then(r => r.text());

// ═══════════════════════════════════════════════════════════
// RECIPE CACHE — Skip the planner entirely for known templates
// ═══════════════════════════════════════════════════════════

const RECIPE_CACHE = {
  // Key: normalized template name → Value: pre-built assembly plan JSON
  // These are the most common requests, pre-computed to save API costs.
  // When a user selects a template from the onboarding grid (not freeform),
  // check here FIRST. If found, skip the API call entirely.
  
  // Example entry (full plans would be stored in /spec/cached_plans/):
  // "quarterly-earnings": { ... full assembly plan JSON ... },
  // "cim": { ... },
  // "board-update": { ... },
};

// ═══════════════════════════════════════════════════════════
// TEMPLATE DETECTION — Check if we can skip the API call
// ═══════════════════════════════════════════════════════════

const TEMPLATE_KEYWORDS = {
  "quarterly-earnings": ["earnings", "quarterly report", "Q1", "Q2", "Q3", "Q4", "revenue report"],
  "cim": ["CIM", "confidential information memo", "information memorandum", "sell-side"],
  "board-update": ["board update", "board deck", "investor update", "board meeting"],
  "investment-memo": ["investment memo", "invest memo", "deal memo"],
  "fundraising-pitch": ["pitch deck", "fundraising", "raise", "pre-seed", "seed", "series A"],
  "sales-proposal": ["proposal", "sales deck", "SOW", "scope of work"],
  "property-listing": ["listing", "property", "real estate", "CMA"],
  "impact-report": ["annual report", "impact report", "nonprofit"],
  "product-demo": ["product demo", "interactive demo", "guided demo"],
  "contract": ["contract", "agreement", "e-sign", "signature"],
};

function detectTemplate(userInput) {
  const lower = userInput.toLowerCase();
  for (const [template, keywords] of Object.entries(TEMPLATE_KEYWORDS)) {
    if (keywords.some(kw => lower.includes(kw.toLowerCase()))) {
      return template;
    }
  }
  return null;
}

// ═══════════════════════════════════════════════════════════
// MAIN FUNCTION — Generate Assembly Plan
// ═══════════════════════════════════════════════════════════

async function generateAssemblyPlan(userInput, options = {}) {
  const {
    apiKey = null,          // Required in standalone mode, handled by platform in embedded mode
    uploadedContent = null, // Text content extracted from uploaded files
    selectedTemplate = null,// Template ID from onboarding grid (bypasses detection)
    forceApi = false,       // Skip cache, always call API
    onStream = null,        // Streaming callback (future)
  } = options;

  // ─── Step 1: Check recipe cache ───
  const templateId = selectedTemplate || detectTemplate(userInput);
  
  if (templateId && RECIPE_CACHE[templateId] && !forceApi) {
    console.log(`[IterFact Planner] Cache hit: ${templateId}`);
    // Clone the cached plan and inject user-provided data
    const plan = structuredClone(RECIPE_CACHE[templateId]);
    plan._source = "cache";
    plan._cost = 0;
    return plan;
  }

  // ─── Step 2: Build the user message ───
  let userMessage = userInput;
  
  if (uploadedContent) {
    userMessage += `\n\n<uploaded_content>\n${uploadedContent}\n</uploaded_content>`;
  }
  
  if (templateId) {
    userMessage += `\n\nThe user selected the "${templateId}" template. Use the matching recipe as a starting point but adapt to their specific request.`;
  }

  // ─── Step 3: Call the API ───
  console.log(`[IterFact Planner] Calling API...`);
  const startTime = Date.now();

  const requestBody = {
    model: CONFIG.model,
    max_tokens: CONFIG.max_tokens,
    temperature: CONFIG.temperature,
    system: SYSTEM_PROMPT,
    messages: [
      { role: "user", content: userMessage }
    ]
  };

  const headers = {
    "Content-Type": "application/json",
    "anthropic-version": "2023-06-01",
  };
  
  // API key only needed in standalone mode
  // In Claude.ai artifacts, auth is handled automatically
  if (apiKey) {
    headers["x-api-key"] = apiKey;
  }

  try {
    const response = await fetch(CONFIG.api_url, {
      method: "POST",
      headers,
      body: JSON.stringify(requestBody),
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(`API Error ${response.status}: ${error.error?.message || JSON.stringify(error)}`);
    }

    const data = await response.json();
    const elapsed = Date.now() - startTime;

    // ─── Step 4: Extract and parse JSON ───
    const rawText = data.content
      .filter(block => block.type === "text")
      .map(block => block.text)
      .join("");

    // Strip markdown fences if model included them despite instructions
    const cleanJson = rawText
      .replace(/^```json?\s*/i, "")
      .replace(/\s*```\s*$/i, "")
      .trim();

    const plan = JSON.parse(cleanJson);

    // ─── Step 5: Validate required fields ───
    validatePlan(plan);

    // ─── Step 6: Attach metadata ───
    plan._source = "api";
    plan._model = data.model;
    plan._cost = estimateCost(data.usage);
    plan._latency_ms = elapsed;
    plan._usage = data.usage;

    console.log(`[IterFact Planner] Plan generated in ${elapsed}ms, cost: $${plan._cost.toFixed(4)}`);
    return plan;

  } catch (error) {
    console.error("[IterFact Planner] Error:", error.message);
    throw error;
  }
}

// ═══════════════════════════════════════════════════════════
// VALIDATION
// ═══════════════════════════════════════════════════════════

function validatePlan(plan) {
  const errors = [];

  if (!plan.meta?.title) errors.push("Missing meta.title");
  if (!plan.theme) errors.push("Missing theme");
  if (!plan.wrapper?.engine) errors.push("Missing wrapper.engine");
  if (!Array.isArray(plan.sections) || plan.sections.length === 0) errors.push("Missing or empty sections");
  
  // Validate wrapper is a known wrapper engine
  const VALID_WRAPPERS = ["01", "02", "04", "12", "18", "21", "24", "25"];
  if (!VALID_WRAPPERS.includes(plan.wrapper.engine)) {
    errors.push(`Invalid wrapper engine: ${plan.wrapper.engine}. Must be one of: ${VALID_WRAPPERS.join(", ")}`);
  }

  // Validate each section
  const VALID_COMPONENTS = ["03","05","06","07","08","09","10","11","13","14","15","16","17","19","20","22","23","24","26","27","28","29","30"];
  
  for (const section of (plan.sections || [])) {
    if (!section.id) errors.push(`Section missing id`);
    if (!section.title) errors.push(`Section ${section.id || '?'} missing title`);
    
    // Must have components or content
    const hasComponents = Array.isArray(section.components) && section.components.length > 0;
    const hasContent = section.content && (section.content.headline || section.content.body);
    if (!hasComponents && !hasContent) {
      errors.push(`Section "${section.id}" has no components and no content`);
    }

    // Validate component engines
    for (const comp of (section.components || [])) {
      if (!VALID_COMPONENTS.includes(comp.engine)) {
        errors.push(`Invalid component engine: ${comp.engine} in section "${section.id}"`);
      }
      if (!comp.instance_id) errors.push(`Component in "${section.id}" missing instance_id`);
    }
  }

  if (errors.length > 0) {
    const err = new Error(`Invalid assembly plan:\n${errors.join("\n")}`);
    err.validationErrors = errors;
    throw err;
  }
}

// ═══════════════════════════════════════════════════════════
// COST ESTIMATION
// ═══════════════════════════════════════════════════════════

function estimateCost(usage) {
  if (!usage) return 0;
  
  // Sonnet 4 pricing (as of March 2026)
  const INPUT_COST_PER_M = 3.00;
  const OUTPUT_COST_PER_M = 15.00;
  
  const inputCost = (usage.input_tokens / 1_000_000) * INPUT_COST_PER_M;
  const outputCost = (usage.output_tokens / 1_000_000) * OUTPUT_COST_PER_M;
  
  return inputCost + outputCost;
}

// ═══════════════════════════════════════════════════════════
// CONTENT PASS — Layer 3 (fills [PLACEHOLDER] fields)
// ═══════════════════════════════════════════════════════════

async function runContentPass(plan, userContext = "", options = {}) {
  const { apiKey = null } = options;

  // Find all [PLACEHOLDER] values in the plan
  const planStr = JSON.stringify(plan);
  const placeholderCount = (planStr.match(/\[PLACEHOLDER[^\]]*\]/g) || []).length;
  
  if (placeholderCount === 0) {
    console.log("[IterFact Content] No placeholders found, skipping content pass");
    return plan;
  }

  console.log(`[IterFact Content] Filling ${placeholderCount} placeholders...`);

  const contentPrompt = `You are the IterFact Content Writer. You receive an assembly plan with [PLACEHOLDER] markers and fill them with polished, professional content.

Rules:
1. Replace every [PLACEHOLDER] and [PLACEHOLDER: hint] with real content matching the hint.
2. Match the tone to the theme and document type.
3. Keep text concise — headlines under 10 words, body paragraphs under 3 sentences.
4. For numeric placeholders, use realistic but clearly fictional data.
5. Output the COMPLETE plan JSON with all placeholders replaced. No other text.

User context: ${userContext || "None provided"}`;

  const headers = {
    "Content-Type": "application/json",
    "anthropic-version": "2023-06-01",
  };
  if (apiKey) headers["x-api-key"] = apiKey;

  const response = await fetch(CONFIG.api_url, {
    method: "POST",
    headers,
    body: JSON.stringify({
      model: CONFIG.model,
      max_tokens: CONFIG.max_tokens,
      temperature: 0.7, // Higher temp for creative content
      system: contentPrompt,
      messages: [
        { role: "user", content: JSON.stringify(plan, null, 2) }
      ]
    }),
  });

  const data = await response.json();
  const rawText = data.content.filter(b => b.type === "text").map(b => b.text).join("");
  const cleanJson = rawText.replace(/^```json?\s*/i, "").replace(/\s*```\s*$/i, "").trim();
  
  const filledPlan = JSON.parse(cleanJson);
  filledPlan._content_pass_cost = estimateCost(data.usage);
  filledPlan._total_cost = (plan._cost || 0) + filledPlan._content_pass_cost;
  
  console.log(`[IterFact Content] Placeholders filled, cost: $${filledPlan._content_pass_cost.toFixed(4)}`);
  return filledPlan;
}

// ═══════════════════════════════════════════════════════════
// FULL PIPELINE — User Input → Assembly Plan → Filled Plan
// ═══════════════════════════════════════════════════════════

async function generateDocument(userInput, options = {}) {
  // Layer 1: Planning
  const plan = await generateAssemblyPlan(userInput, options);
  
  // Layer 3: Content injection (skip if no placeholders)
  const filledPlan = await runContentPass(plan, userInput, options);
  
  // Layer 2 (Assembler) happens AFTER this function returns the plan.
  // The Assembler is a separate module that reads the plan and builds HTML.
  
  return filledPlan;
}

// ═══════════════════════════════════════════════════════════
// EXPORTS
// ═══════════════════════════════════════════════════════════

// For ES modules:
// export { generateAssemblyPlan, runContentPass, generateDocument, detectTemplate, validatePlan };

// For CommonJS:
// module.exports = { generateAssemblyPlan, runContentPass, generateDocument, detectTemplate, validatePlan };

// For browser (attach to window):
if (typeof window !== "undefined") {
  window.IterFactPlanner = {
    generateAssemblyPlan,
    runContentPass,
    generateDocument,
    detectTemplate,
    validatePlan,
  };
}
