/**
 * Simple Chain Pipeline — generated by aiwg nlp new
 * Pattern: simple-chain
 * Dependencies: @anthropic-ai/sdk
 *
 * Install: npm install @anthropic-ai/sdk
 */

import Anthropic from "@anthropic-ai/sdk";
import * as fs from "fs";
import * as path from "path";

// ---------------------------------------------------------------------------
// Configuration
// ---------------------------------------------------------------------------

const PROMPTS_DIR = path.join(__dirname, "..", "prompts");
const MODEL = "claude-haiku-4-5";
const MAX_TOKENS = 512;
const TIMEOUT_MS = 30_000;
const MAX_RETRIES = 3;
const RETRY_ON_STATUS = new Set([429, 502, 503]);
const BACKOFF_MS = 1_000;

// ---------------------------------------------------------------------------
// Prompt loading
// ---------------------------------------------------------------------------

interface Prompt {
  system: string;
  user: string;
}

function loadPrompt(filename: string, variables: Record<string, string>): Prompt {
  const filePath = path.join(PROMPTS_DIR, filename);
  let content = fs.readFileSync(filePath, "utf-8");

  // Strip YAML frontmatter
  if (content.startsWith("---")) {
    const parts = content.split("---");
    content = parts.slice(2).join("---");
  }

  // Parse ## System and ## User sections
  let system = "";
  let user = "";
  let currentSection: "system" | "user" | null = null;

  for (const line of content.split("\n")) {
    if (line.trim() === "## System") {
      currentSection = "system";
    } else if (line.trim() === "## User") {
      currentSection = "user";
    } else if (currentSection === "system") {
      system += line + "\n";
    } else if (currentSection === "user") {
      user += line + "\n";
    }
  }

  // Substitute variables
  for (const [key, value] of Object.entries(variables)) {
    system = system.replaceAll(`{{${key}}}`, value);
    user = user.replaceAll(`{{${key}}}`, value);
  }

  return { system: system.trim(), user: user.trim() };
}

// ---------------------------------------------------------------------------
// LLM call with retry
// ---------------------------------------------------------------------------

async function callLLM(
  client: Anthropic,
  system: string,
  user: string,
  model: string = MODEL,
  maxTokens: number = MAX_TOKENS,
): Promise<string> {
  let lastError: Error | undefined;

  for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
    try {
      const response = await client.messages.create({
        model,
        max_tokens: maxTokens,
        system,
        messages: [{ role: "user", content: user }],
      });

      const block = response.content[0];
      if (block.type !== "text") throw new Error("Unexpected content block type");
      return block.text;
    } catch (err: unknown) {
      const status = (err as { status?: number }).status;
      if (status !== undefined && RETRY_ON_STATUS.has(status)) {
        lastError = err as Error;
        await new Promise((resolve) => setTimeout(resolve, BACKOFF_MS * 2 ** attempt));
      } else {
        throw err;
      }
    }
  }

  throw new Error(`LLM call failed after ${MAX_RETRIES} attempts: ${lastError?.message}`);
}

// ---------------------------------------------------------------------------
// Output validation
// ---------------------------------------------------------------------------

function validateOutput<T>(raw: string): T {
  let cleaned = raw.trim();
  // Strip markdown code fences if present
  if (cleaned.startsWith("```")) {
    const lines = cleaned.split("\n");
    cleaned = lines
      .slice(1, lines[lines.length - 1] === "```" ? -1 : undefined)
      .join("\n");
  }
  try {
    return JSON.parse(cleaned) as T;
  } catch (e) {
    throw new Error(`Output is not valid JSON: ${e}\nRaw: ${cleaned.slice(0, 200)}`);
  }
}

// ---------------------------------------------------------------------------
// Pipeline
// ---------------------------------------------------------------------------

interface PipelineOutput {
  // TODO: define your output fields here
  [key: string]: unknown;
}

export async function run(inputText: string): Promise<PipelineOutput> {
  const client = new Anthropic({ timeout: TIMEOUT_MS });

  // Step: extract
  const { system, user } = loadPrompt("generator.prompt.md", { input_text: inputText });
  const raw = await callLLM(client, system, user);
  const output = validateOutput<PipelineOutput>(raw);

  // Add additional steps here:
  // const { system: s2, user: u2 } = loadPrompt("step2.prompt.md", { field: output.field });
  // const raw2 = await callLLM(client, s2, u2);
  // const output2 = validateOutput<Step2Output>(raw2);

  return output;
}

// ---------------------------------------------------------------------------
// Entry point
// ---------------------------------------------------------------------------

if (require.main === module) {
  const input = process.argv[2];
  if (!input) {
    console.error("Usage: ts-node pipeline.ts '<input text>'");
    process.exit(1);
  }
  run(input)
    .then((result) => console.log(JSON.stringify(result, null, 2)))
    .catch((err) => {
      console.error(err);
      process.exit(1);
    });
}
