/**
 * engine/qa-runners/maestro.js — P-c4a9e7f3
 *
 * Maestro built-in runner adapter for QA Sessions. Registered at boot by
 * engine/qa-runners.js (_registerBuiltins). Priority 80 — higher than the
 * Playwright safe default so a project with `.maestro/` wins on tie.
 *
 * Detection: project.localPath (or target.worktree for kind='current') has
 * a `.maestro/` directory. Explicit `runner: 'maestro'` is honoured directly
 * by qa-runners.js#detectRunner before detect() runs, so it does not need
 * a branch here.
 *
 * Hooks: see engine/qa-runners.js header for the contract. We return
 * markdown strings from generateBrief / executeBrief — those flow into the
 * qa-session-draft.md / qa-session-execute.md playbooks as {{runner_brief}}
 * and {{runner_execute_brief}} (wired in P-f9a2e1b4).
 */

const fs = require('fs');
const path = require('path');

const NAME = 'maestro';
const PRIORITY = 80;
const INSTALL_HINT =
  'Maestro CLI is required. Install via Homebrew (`brew tap mobile-dev-inc/tap && brew install maestro`) ' +
  'on macOS/Linux, or follow https://maestro.mobile.dev/getting-started/installing-maestro for Windows / manual install. ' +
  'Confirm with `maestro --version` before re-running the session.';

function _resolveProbeRoot(target, project) {
  if (target && target.kind === 'current' && typeof target.worktree === 'string' && target.worktree) {
    return target.worktree;
  }
  if (project && typeof project.localPath === 'string' && project.localPath) {
    return project.localPath;
  }
  return null;
}

function detect(target, project) {
  const root = _resolveProbeRoot(target, project);
  if (!root) return false;
  try {
    const stat = fs.statSync(path.join(root, '.maestro'));
    return stat.isDirectory();
  } catch {
    return false;
  }
}

function _spawnUrl(spawnInfo) {
  if (!spawnInfo) return '';
  if (spawnInfo.attrs && typeof spawnInfo.attrs.base_url === 'string') return spawnInfo.attrs.base_url;
  if (Array.isArray(spawnInfo.ports) && spawnInfo.ports.length > 0) {
    return `http://localhost:${spawnInfo.ports[0]}`;
  }
  return '';
}

function generateBrief(opts) {
  opts = opts || {};
  const baseUrl = _spawnUrl(opts.spawnInfo);
  const flows = opts.flowsRaw || '(no flows provided)';
  const sessionId = (opts.session && opts.session.id) || (opts.sessionId) || '<session-id>';
  const capture = opts.capture || {};
  const captureSummary = Object.entries(capture).filter(([, v]) => !!v).map(([k]) => k).join(', ') || '(none requested)';

  return [
    '## Maestro test draft',
    '',
    `Write a single Maestro flow file at \`engine/qa-tests/${sessionId}/flow.yaml\` that exercises:`,
    '',
    flows.trim(),
    '',
    '### Required structure',
    '',
    '- Top-level `appId:` set to the package id Maestro should drive (Android package or iOS bundle).',
    '- A sequence of Maestro commands (`launchApp`, `tapOn`, `inputText`, `assertVisible`, …) that maps 1:1 to the flow above.',
    baseUrl
      ? `- For any HTTP step, use the live target base URL \`${baseUrl}\` (resolved from the managed-spawn).`
      : '- The managed-spawn target did not advertise a base URL; rely on the app id alone.',
    '- Keep the flow under 50 lines — split into named sub-flows (`runFlow:`) when the user described independent journeys.',
    '',
    '### Capture configuration',
    '',
    `Requested artifacts: ${captureSummary}. Surface them via the standard Maestro recording flags ` +
      '(`maestro record` or `maestro test --output …`) in the EXECUTE phase; do NOT enable them inside the flow YAML itself.',
    '',
    'See https://maestro.mobile.dev/api-reference/commands for the command vocabulary.',
  ].join('\n');
}

function executeBrief(opts) {
  opts = opts || {};
  const sessionId = (opts.session && opts.session.id) || (opts.sessionId) || '<session-id>';
  const testFile = opts.testFile || `engine/qa-tests/${sessionId}/flow.yaml`;
  const capture = opts.capture || {};
  const outputDir = `engine/qa-artifacts/${sessionId}`;

  const cmdFlags = [`--output ${outputDir}`];
  if (capture.logs) cmdFlags.push(`--debug-output ${outputDir}/maestro-debug`);
  // `maestro test` does not support video; surface the limitation in the
  // brief body rather than silently dropping the capture request.

  return [
    '## Maestro execute',
    '',
    'Run the drafted flow against the live managed-spawn:',
    '',
    '```bash',
    `maestro test ${testFile} ${cmdFlags.join(' ')}`,
    '```',
    '',
    `Write the result sidecar to \`agents/<your-id>/qa-run-result.json\` referencing artifacts under \`${outputDir}/\`.`,
    capture.video
      ? '\nVideo capture was requested — note in the result summary that `maestro test` does not record video and the human will need a separate `maestro record` pass if they want it.'
      : '',
    capture.screenshots
      ? '\nScreenshot capture was requested — add `takeScreenshot:` steps at the key flow boundaries; Maestro writes them under the --output dir.'
      : '',
  ].filter(Boolean).join('\n');
}

function validateOutputDir(dir) {
  const errors = [];
  if (typeof dir !== 'string' || dir === '') {
    return { ok: false, errors: ['dir must be a non-empty string'] };
  }
  let entries;
  try { entries = fs.readdirSync(dir); }
  catch (err) {
    return { ok: false, errors: [`qa-tests dir ${dir} unreadable: ${err.message}`] };
  }
  const yamlFiles = entries.filter(f => f.endsWith('.yaml') || f.endsWith('.yml'));
  if (yamlFiles.length === 0) {
    errors.push(`no Maestro flow file (*.yaml) found in ${dir}`);
  }
  return { ok: errors.length === 0, errors };
}

module.exports = {
  name: NAME,
  spec: {
    priority: PRIORITY,
    label: 'Maestro',
    description: 'Mobile-first NL flows. Detects when the target project has a .maestro/ directory.',
    installHint: INSTALL_HINT,
    detect,
    generateBrief,
    executeBrief,
    validateOutputDir,
  },
};
