/**
 * recording-flush.ts
 *
 * Called at task end to:
 *   1. Flush any buffered recorder actions to recordings/{recordTo}.json.
 *   2. If recordTo ends with ".next", compile a candidate patch script at
 *      scripts/{baseName}.next.js using the source script's description and
 *      params as hints (self-healing patch staging).
 *
 * This helper is intentionally non-throwing — all errors are logged and
 * swallowed so a flush bug cannot crash the agent loop.
 */

import { readFileSync, writeFileSync, renameSync, existsSync, mkdirSync } from 'fs';
import { join, dirname } from 'path';
import { recorder } from '../engine/browser-recorder.js';
import { parseScript, serializeScript } from './frontmatter.js';
import { compileScriptBody } from './script-compiler.js';
import { resolveUserSubdir } from './paths.js';
import { logger } from '../logger.js';
import type { RecordedAction } from '../engine/browser-recorder.js';

export function flushAndStageRecording(
  taskId: string,
  ownerId: string | undefined,
  userFolderRoot: string,
): void {
  if (!ownerId) return;
  const recordTo = recorder.recordTo(taskId);
  if (!recordTo) return;

  const flushedPath = recorder.flush(taskId, userFolderRoot, ownerId);
  if (!flushedPath) return;

  if (!recordTo.endsWith('.next')) {
    logger.debug(`[recording] flushed regular recording: ${recordTo}.json`);
    return;
  }

  // Self-healing patch: compile a candidate .next.js from the flushed trace.
  const baseName = recordTo.slice(0, -'.next'.length);
  try {
    const sourceScriptPath = resolveUserSubdir(
      userFolderRoot,
      ownerId,
      'browser-macros',
      `${baseName}.js`,
    );
    if (!existsSync(sourceScriptPath)) {
      logger.warn(
        `[recording] cannot stage patch — source script ${baseName}.js missing`,
      );
      return;
    }

    const sourceText = readFileSync(sourceScriptPath, 'utf-8');
    const sourceParsed = parseScript(sourceText);

    const recordingPayload = JSON.parse(readFileSync(flushedPath, 'utf-8')) as {
      actions: RecordedAction[];
    };

    // Compile just the body (we build frontmatter ourselves to control timestamps)
    const { body, paramSpecs } = compileScriptBody({
      recording: recordingPayload.actions,
      description: sourceParsed.frontmatter.description,
      sessionProfileId: sourceParsed.frontmatter.sessionProfileId,
      // We don't know which fill values map to which params after a recovery session.
      // Pass no paramHints so the patch contains literal values; the user will
      // manually re-parameterize during diff review.
      paramHints: [],
      recordingSource: `${recordTo}.json`,
    });

    const now = new Date().toISOString();
    const patched = serializeScript({
      frontmatter: {
        description: sourceParsed.frontmatter.description,
        params: paramSpecs,
        sessionProfileId: sourceParsed.frontmatter.sessionProfileId,
        recordingSource: `${recordTo}.json`,
        // Preserve original createdAt; stamp fresh updatedAt.
        createdAt: sourceParsed.frontmatter.createdAt ?? now,
        updatedAt: now,
      },
      body,
    });

    const targetPath = resolveUserSubdir(
      userFolderRoot,
      ownerId,
      'browser-macros',
      `${baseName}.next.js`,
    );
    const tmpPath = `${targetPath}.tmp-${process.pid}-${Date.now()}`;
    mkdirSync(dirname(targetPath), { recursive: true });
    writeFileSync(tmpPath, patched, { encoding: 'utf-8', mode: 0o600 });
    renameSync(tmpPath, targetPath);
    logger.info(`[user-folder] staged self-healing patch: browser-macros/${baseName}.next.js`);
  } catch (e) {
    logger.warn(`[recording] failed to stage patch: ${(e as Error).message}`);
  }
}
