#!/usr/bin/env -S npx --yes tsx
// =============================================================================
// init.ts — Initialize a data store from a YAML definition
//
// Part of: .opencode/skills/templating-vault/scripts/
//
// Creates or validates a data directory from a YAML definition file.
// Handles three modes for existing stores: ignore, validate, fix.
//
// USAGE:
//   init.ts --source <definition.yaml> --target <data_dir>
//           [--on-exists ignore|validate|fix]
//
// DEFINITION FORMAT:
//   defaults:
//     literal: true
//     type: string
//   keys:
//     credentials.forgejo_pat:
//       literal: true
//       write_once: true
//       type: hex
//       description: "Primary bot Forgejo PAT"
//       value: "preset_value"         # optional preset value
//     config.max_workers:
//       literal: false
//       type: positive_integer
//       on_absent: "4"
//
// OUTPUT (stdout): JSON result
// =============================================================================
import * as fs from 'node:fs';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';
import yaml from 'js-yaml';
import {
  parseCommonFlags, getFlag, output, log, atomicWrite,
} from './api.ts';

interface KeyDefinition {
  literal?: boolean;
  readonly?: boolean;
  write_once?: boolean;
  type?: string;
  validate?: { regex?: string; message?: string };
  template?: boolean;
  on_absent?: string | number;
  description?: string;
  value?: string;           // optional preset value
  owner?: string;
}

interface StoreDefinition {
  defaults?: Record<string, unknown>;
  keys?: Record<string, KeyDefinition>;
}

async function main(): Promise<void> {
  const { rest } = parseCommonFlags(process.argv);

  const source = getFlag(rest, '--source');
  const target = getFlag(rest, '--target');
  const onExists = (getFlag(rest, '--on-exists') ?? 'ignore') as 'ignore' | 'validate' | 'fix';

  if (!source) {
    output({ initialized: false, error: '--source is required' });
    process.exit(1);
  }
  if (!target) {
    output({ initialized: false, error: '--target is required' });
    process.exit(1);
  }

  if (!fs.existsSync(source)) {
    output({ initialized: false, error: `Source file "${source}" not found` });
    process.exit(1);
  }

  const definition = yaml.load(fs.readFileSync(source, 'utf-8')) as StoreDefinition;
  if (!definition) {
    output({ initialized: false, error: 'Empty or invalid YAML definition' });
    process.exit(1);
  }

  const targetExists = fs.existsSync(target);

  if (targetExists && onExists === 'ignore') {
    output({ initialized: true, action: 'ignored', message: 'Store already exists' });
    return;
  }

  // Create target directory
  fs.mkdirSync(target, { recursive: true });

  const actions: string[] = [];
  const errors: string[] = [];

  // Write .store.yaml with defaults
  if (definition.defaults) {
    const storeYamlPath = path.join(target, '.store.yaml');
    if (!targetExists || onExists === 'fix' || !fs.existsSync(storeYamlPath)) {
      atomicWrite(storeYamlPath, yaml.dump({ defaults: definition.defaults }, { lineWidth: -1 }));
      actions.push('wrote .store.yaml');
    } else if (onExists === 'validate') {
      // Check if existing .store.yaml matches
      const existing = fs.existsSync(storeYamlPath)
        ? yaml.load(fs.readFileSync(storeYamlPath, 'utf-8'))
        : null;
      if (JSON.stringify(existing) !== JSON.stringify({ defaults: definition.defaults })) {
        errors.push('.store.yaml diverges from definition');
      }
    }
  }

  // Process keys
  if (definition.keys) {
    for (const [keyPath, keyDef] of Object.entries(definition.keys)) {
      const segments = keyPath.split('.');
      const keyDir = path.join(target, ...segments);

      // Create directory
      fs.mkdirSync(keyDir, { recursive: true });

      // Write meta.yaml
      const metaPath = path.join(keyDir, 'meta.yaml');
      const metaContent: Record<string, unknown> = {};
      if (keyDef.literal !== undefined) metaContent.literal = keyDef.literal;
      if (keyDef.readonly !== undefined) metaContent.readonly = keyDef.readonly;
      if (keyDef.write_once !== undefined) metaContent.write_once = keyDef.write_once;
      if (keyDef.type !== undefined) metaContent.type = keyDef.type;
      if (keyDef.validate !== undefined) metaContent.validate = keyDef.validate;
      if (keyDef.template !== undefined) metaContent.template = keyDef.template;
      if (keyDef.on_absent !== undefined) metaContent.on_absent = keyDef.on_absent;
      if (keyDef.description !== undefined) metaContent.description = keyDef.description;
      if (keyDef.owner !== undefined) metaContent.owner = keyDef.owner;

      if (Object.keys(metaContent).length > 0) {
        if (!targetExists || onExists === 'fix' || !fs.existsSync(metaPath)) {
          atomicWrite(metaPath, yaml.dump(metaContent, { lineWidth: -1 }));
          actions.push(`wrote meta for ${keyPath}`);
        } else if (onExists === 'validate') {
          const existing = fs.existsSync(metaPath)
            ? yaml.load(fs.readFileSync(metaPath, 'utf-8'))
            : null;
          if (JSON.stringify(existing) !== JSON.stringify(metaContent)) {
            errors.push(`${keyPath}: meta.yaml diverges from definition`);
          }
        }
      }

      // Write preset value if defined
      if (keyDef.value !== undefined) {
        const valuePath = path.join(keyDir, 'value');
        if (!targetExists || onExists === 'fix' || !fs.existsSync(valuePath)) {
          atomicWrite(valuePath, String(keyDef.value));
          actions.push(`wrote value for ${keyPath}`);
        } else if (onExists === 'validate') {
          const existing = fs.existsSync(valuePath)
            ? fs.readFileSync(valuePath, 'utf-8')
            : null;
          if (existing !== String(keyDef.value)) {
            errors.push(`${keyPath}: value diverges from definition`);
          }
        }
      }
    }
  }

  if (onExists === 'validate' && errors.length > 0) {
    output({ initialized: false, action: 'validate', errors });
    process.exit(1);
  }

  output({
    initialized: true,
    action: targetExists ? onExists : 'created',
    target,
    actions,
  });
}

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