#!/usr/bin/env -S npx --yes tsx
// =============================================================================
// set.ts — Store a value in the vault
//
// Part of: .opencode/skills/templating-vault/scripts/
//
// Stores a value at a dot-separated key. Enforces:
//   - Literal keys can only be set via extract.ts (rejected here)
//   - Write-once keys cannot be overwritten
//   - Type and regex validation
//   - Owner-based access control
//
// USAGE:
//   set.ts --key <key> --value <value> [--owner <id>] [--write-once]
//          [--literal] [--force-literal] [--owner-overwrite]
//          [--type <type>] [--description <desc>]
//          [--config <path>] [--store-dir <path>]
//
// OUTPUT (stdout): JSON result
// =============================================================================
import { fileURLToPath } from 'node:url';
import {
  parseCommonFlags, getFlag, hasFlag, output, log,
  loadConfig, resolveStore, loadKeyMeta, saveKeyMeta,
  readValue, writeValue, hasValue, 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 key = getFlag(rest, '--key');
  const value = getFlag(rest, '--value');
  const owner = getFlag(rest, '--owner');
  const writeOnce = hasFlag(rest, '--write-once');
  const forceLiteral = hasFlag(rest, '--force-literal'); // internal: used by extract.ts
  const setLiteralFlag = hasFlag(rest, '--literal'); // set literal in metadata
  const ownerOverwrite = hasFlag(rest, '--owner-overwrite');
  const typeFlag = getFlag(rest, '--type');
  const description = getFlag(rest, '--description');

  if (!key) {
    output({ stored: false, error: '--key is required' });
    process.exit(1);
  }
  if (value === undefined) {
    output({ stored: false, error: '--value is required' });
    process.exit(1);
  }

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

  // Load existing metadata
  const meta = loadKeyMeta(config, key);

  // Literal enforcement: reject direct set on literal keys unless:
  //   - --force-literal is set (internal flag used by extract.ts)
  //   - Store access is 'admin' (bootstrap/setup mode)
  if (meta.literal && !forceLiteral && resolved.mapping.access !== 'admin') {
    output({
      stored: false,
      error: `Cannot directly set literal variable "${key}". Use extract.ts with a regex instead, or use admin access to bootstrap.`,
      key,
      literal: true,
    });
    process.exit(1);
  }

  // Write-once enforcement
  if (meta.write_once && hasValue(config, key)) {
    if (ownerOverwrite && meta.owner && meta.owner === owner) {
      // Owner can overwrite write-once keys
      log(`Owner-overwrite: updating write-once key "${key}"`);
    } else {
      output({
        stored: false,
        error: `Key "${key}" is write-once and already has a value`,
        key,
        write_once: true,
      });
      process.exit(1);
    }
  }

  // Readonly enforcement
  if (meta.readonly && hasValue(config, key) && resolved.mapping.access !== 'admin') {
    output({
      stored: false,
      error: `Key "${key}" is readonly`,
      key,
      readonly: true,
    });
    process.exit(1);
  }

  // Validation
  const validationError = validateValue(value, meta);
  if (validationError) {
    output({ stored: false, error: `Validation failed for "${key}": ${validationError}`, key });
    process.exit(1);
  }

  // Write value
  writeValue(config, key, value);

  // Update metadata if flags provided
  const updatedMeta: KeyMeta = { ...meta };
  if (writeOnce) updatedMeta.write_once = true;
  if (setLiteralFlag) updatedMeta.literal = true;
  if (owner) updatedMeta.owner = owner;
  if (typeFlag) updatedMeta.type = typeFlag;
  if (description) updatedMeta.description = description;

  // Only save meta if something changed from defaults or explicitly provided
  if (writeOnce || setLiteralFlag || owner || typeFlag || description) {
    saveKeyMeta(config, key, updatedMeta);
  }

  output({ stored: true, key, write_once: updatedMeta.write_once ?? false });
}

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