#!/usr/bin/env -S npx --yes tsx
// =============================================================================
// extract.ts — Extract a value from source text using regex and store it
//
// Part of: .opencode/skills/templating-vault/scripts/
//
// The ONLY way to set literal variables. The LLM provides a regex pattern;
// this script applies it against the source text (from a vault key or file)
// and stores the captured value. The LLM never sees or reproduces the raw
// value — it only provides the regex.
//
// USAGE:
//   extract.ts --source-key <key> --key <target_key> --regex <pattern>
//              [--capture-group <n>] [--owner <id>]
//              [--config <path>] [--store-dir <path>]
//
//   extract.ts --source-file <path> --key <target_key> --regex <pattern>
//              [--capture-group <n>] [--owner <id>]
//              [--config <path>] [--store-dir <path>]
//
// OUTPUT (stdout): JSON result
// =============================================================================
import * as fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import {
  parseCommonFlags, getFlag, hasFlag, output, log,
  loadConfig, readValue, resolveStore, writeValue,
  loadKeyMeta, saveKeyMeta, validateValue,
  type KeyMeta,
} from './api.ts';

async function main(): Promise<void> {
  const { config: cfgPath, storeDirs, rest } = parseCommonFlags(process.argv);
  const config = loadConfig(cfgPath, storeDirs.length > 0 ? storeDirs : undefined);

  const sourceKey = getFlag(rest, '--source-key');
  const sourceFile = getFlag(rest, '--source-file');
  const targetKey = getFlag(rest, '--key');
  const regexStr = getFlag(rest, '--regex');
  const captureGroupStr = getFlag(rest, '--capture-group');
  const owner = getFlag(rest, '--owner');
  const writeOnce = hasFlag(rest, '--write-once');

  if (!targetKey) {
    output({ extracted: false, error: '--key is required' });
    process.exit(1);
  }
  if (!regexStr) {
    output({ extracted: false, error: '--regex is required' });
    process.exit(1);
  }
  if (!sourceKey && !sourceFile) {
    output({ extracted: false, error: '--source-key or --source-file is required' });
    process.exit(1);
  }

  // Check access
  const resolved = resolveStore(config, targetKey);
  if (resolved.mapping.access === 'readonly') {
    output({ extracted: false, error: `Store for prefix "${resolved.mapping.prefix}" is readonly` });
    process.exit(1);
  }

  // Load source text
  let sourceText: string;
  if (sourceKey) {
    const val = readValue(config, sourceKey);
    if (val === null) {
      output({ extracted: false, error: `Source key "${sourceKey}" not found in vault` });
      process.exit(1);
    }
    sourceText = val;
  } else {
    if (!fs.existsSync(sourceFile!)) {
      output({ extracted: false, error: `Source file "${sourceFile}" not found` });
      process.exit(1);
    }
    sourceText = fs.readFileSync(sourceFile!, 'utf-8');
  }

  // Apply regex
  const captureGroup = captureGroupStr ? parseInt(captureGroupStr, 10) : 1;
  let regex: RegExp;
  try {
    regex = new RegExp(regexStr, 'm');
  } catch (err) {
    output({
      extracted: false,
      error: `Invalid regex: ${err instanceof Error ? err.message : String(err)}`,
    });
    process.exit(1);
  }

  const match = regex.exec(sourceText);
  if (!match) {
    // Provide context hints without exposing full source
    const lines = sourceText.split('\n');
    output({
      extracted: false,
      error: 'Regex did not match any text in the source',
      hints: {
        source_lines: lines.length,
        source_chars: sourceText.length,
        first_200_chars: sourceText.slice(0, 200),
        regex_used: regexStr,
      },
    });
    process.exit(1);
  }

  if (captureGroup >= match.length) {
    output({
      extracted: false,
      error: `Capture group ${captureGroup} does not exist. Regex matched ${match.length - 1} group(s).`,
    });
    process.exit(1);
  }

  const extractedValue = match[captureGroup] ?? match[0];

  // Check write-once on target
  const meta = loadKeyMeta(config, targetKey);
  if (meta.write_once && readValue(config, targetKey) !== null) {
    output({
      extracted: false,
      error: `Key "${targetKey}" is write-once and already has a value`,
    });
    process.exit(1);
  }

  // Validate
  const validationError = validateValue(extractedValue, meta);
  if (validationError) {
    output({
      extracted: false,
      error: `Validation failed for "${targetKey}": ${validationError}`,
      extracted_length: extractedValue.length,
    });
    process.exit(1);
  }

  // Store value (using --force-literal internally to bypass literal check in writeValue)
  writeValue(config, targetKey, extractedValue);

  // Update metadata
  const updatedMeta: KeyMeta = { ...meta };
  if (owner) updatedMeta.owner = owner;
  if (writeOnce) updatedMeta.write_once = true;
  if (owner || writeOnce) {
    saveKeyMeta(config, targetKey, updatedMeta);
  }

  output({
    extracted: true,
    key: targetKey,
    stored: true,
    value_length: extractedValue.length,
  });
}

if (process.argv[1] === fileURLToPath(import.meta.url)) {
  main().catch((err: unknown) => {
    output({ extracted: false, error: String(err instanceof Error ? err.message : err) });
    process.exit(1);
  });
}
