'use strict';

const fs = require('node:fs');
const path = require('node:path');
const crypto = require('node:crypto');

const MANAGED_REGION_START = '<!-- okf-project:managed:start -->';
const MANAGED_REGION_END = '<!-- okf-project:managed:end -->';

const DEFAULT_TARGETS = Object.freeze({
  CLAUDE_MEMORY: '.claude/memory.md',
  MEMORY_MD: 'MEMORY.md',
  CLAUDE_MD: 'CLAUDE.md',
  AGENTS_MD: 'AGENTS.md',
});

function escapeRegExp(value) {
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

function normalizeLineEndings(value) {
  return String(value ?? '').replace(/\r\n?/g, '\n');
}

function buildManagedBlock(content) {
  return [
    MANAGED_REGION_START,
    content.trimEnd(),
    MANAGED_REGION_END,
  ].join('\n');
}

function replaceManagedRegion(existingContent, managedContent) {
  const normalizedExisting = normalizeLineEndings(existingContent);
  const managedBlock = buildManagedBlock(managedContent);
  const regionPattern = new RegExp(
    `${escapeRegExp(MANAGED_REGION_START)}[\\s\\S]*?${escapeRegExp(MANAGED_REGION_END)}`,
    'm',
  );

  if (regionPattern.test(normalizedExisting)) {
    return normalizedExisting.replace(regionPattern, managedBlock);
  }

  if (normalizedExisting.length === 0) {
    return `${managedBlock}\n`;
  }

  return `${normalizedExisting.replace(/\n*$/, '\n\n')}${managedBlock}\n`;
}

function renderMemoryProjection(context) {
  return [
    '# Open Knowledge Format Memory',
    '',
    `Project: ${context.projectName}`,
    `Project root: ${context.projectRoot}`,
    `OKF root: ${context.okfRoot}`,
    '',
    '- Start navigation at `index.md` and descend through local indexes before opening concept files.',
    '- Use frontmatter metadata to filter targets before reading full documents.',
    '- Keep OKF updates single-concept and update the parent `index.md` when adding new entries.',
  ].join('\n');
}

function renderInstructionProjection(context) {
  return [
    '# Repository Navigation Protocol (Open Knowledge Format)',
    '',
    `This environment uses the Open Knowledge Format for project \`${context.projectName}\`.`,
    `Project root: \`${context.projectRoot}\``,
    `OKF root: \`${context.okfRoot}\``,
    '',
    '1. Start at `index.md` and navigate level-by-level through local indexes.',
    '2. Check frontmatter metadata before reading a full document body.',
    '3. Keep each new knowledge file focused on one concept and update the parent `index.md` immediately.',
  ].join('\n');
}

function renderProjection(targetName, context) {
  if (targetName === 'CLAUDE_MEMORY' || targetName === 'MEMORY_MD') {
    return renderMemoryProjection(context);
  }

  return renderInstructionProjection(context);
}

function writeFileAtomic(filePath, content) {
  fs.mkdirSync(path.dirname(filePath), { recursive: true });

  const tempPath = path.join(
    path.dirname(filePath),
    `.okf-project.${process.pid}.${Date.now()}.${crypto.randomBytes(6).toString('hex')}.tmp`,
  );

  fs.writeFileSync(tempPath, content, 'utf8');
  fs.renameSync(tempPath, filePath);
}

function projectOkfToHome(options = {}) {
  const homeDir = options.homeDir;
  const projectName = options.projectName;
  const projectRoot = options.projectRoot;
  const okfRoot = options.okfRoot;
  const targets = { ...DEFAULT_TARGETS, ...(options.targets || {}) };

  if (typeof homeDir !== 'string' || homeDir.length === 0) {
    throw new Error('homeDir is required');
  }

  if (typeof projectName !== 'string' || projectName.length === 0) {
    throw new Error('projectName is required');
  }

  if (typeof projectRoot !== 'string' || projectRoot.length === 0) {
    throw new Error('projectRoot is required');
  }

  if (typeof okfRoot !== 'string' || okfRoot.length === 0) {
    throw new Error('okfRoot is required');
  }

  const context = { projectName, projectRoot, okfRoot };
  const files = {};

  for (const [targetName, relativePath] of Object.entries(targets)) {
    const filePath = path.join(homeDir, relativePath);
    const nextContent = replaceManagedRegion(
      fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : '',
      renderProjection(targetName, context),
    );
    const previousContent = fs.existsSync(filePath) ? normalizeLineEndings(fs.readFileSync(filePath, 'utf8')) : '';
    const changed = previousContent !== nextContent;

    if (changed) {
      writeFileAtomic(filePath, nextContent);
    }

    files[targetName] = {
      path: filePath,
      changed,
    };
  }

  return { files };
}

module.exports = {
  DEFAULT_TARGETS,
  MANAGED_REGION_START,
  MANAGED_REGION_END,
  projectOkfToHome,
  replaceManagedRegion,
};
