const fs = require('node:fs');
const path = require('node:path');
const { clampNumber } = require('./lib/json_utils.cjs');
const { createWorkspace, resolveVideoPath, writeJson, emitEvent } = require('./lib/workspace.cjs');
const { extractFrames } = require('./lib/frame_extractor.cjs');
const { resolveModelConfig } = require('./lib/vision_client.cjs');
const { detectDamageCandidates } = require('./lib/damage_detector.cjs');
const { groundDamages } = require('./lib/damage_grounding.cjs');
const { reviewDamageAnnotations } = require('./lib/damage_reviewer.cjs');
const { selectBestFrames } = require('./lib/best_frame_selector.cjs');

function normalizeInput(raw) {
  if (!raw || typeof raw !== 'object') throw new Error('Input JSON object is required');
  if (!raw.videoUrl) throw new Error('videoUrl is required');
  return {
    videoUrl: String(raw.videoUrl),
    taskId: raw.taskId ? String(raw.taskId) : undefined,
    fps: clampNumber(raw.fps, 0.2, 10, 5),
    quality: clampNumber(raw.quality, 1, 100, 90),
    batchSize: clampNumber(raw.batchSize, 1, 80, 50),
    concurrency: clampNumber(raw.concurrency, 1, 8, 5),
    groundingWindow: clampNumber(raw.groundingWindow, 0.5, 5, 2),
    groundingFrameLimit: clampNumber(raw.groundingFrameLimit, 1, 10, 5),
    reviewConcurrency: clampNumber(raw.reviewConcurrency, 1, 6, 3),
    topN: clampNumber(raw.topN, 1, 5, 1),
    mode: ['full', 'frames-only', 'detect-only'].includes(raw.mode) ? raw.mode : 'full',
  };
}

function readPrompt(name) {
  return fs.readFileSync(path.join(__dirname, '..', 'prompts', name), 'utf8');
}

function resolveWorkspaceRoot(env) {
  if (env.RZYX_AI_WORKSPACE_ROOT) return env.RZYX_AI_WORKSPACE_ROOT;
  if (env.RZYX_AI_DATA_DIR) return path.join(env.RZYX_AI_DATA_DIR, 'workspace', 'vehicle-damage-inspection');
  return path.join(__dirname, '..', '.workspace');
}

function buildFinalOutput({ workspace, videoInfo, vehicleInfo, candidates, damages, bestFrameImages, reviewImages, uncertainDamages = [], artifacts }) {
  return {
    success: true,
    taskId: workspace.taskId,
    workspacePath: workspace.workspacePath,
    summary: {
      duration: videoInfo.duration,
      resolution: videoInfo.resolution,
      frameCount: videoInfo.extractedFrames,
      candidateDamageCount: candidates.length,
      mergedDamageCount: damages.length,
      uncertainDamageCount: uncertainDamages.length,
      bestFrameCount: bestFrameImages.length,
      reviewImageCount: reviewImages.length,
      needsReview: reviewImages.length > 0,
    },
    vehicleInfo,
    damages,
    uncertainDamages,
    bestFrameImages,
    reviewImages,
    artifacts,
  };
}

async function run(rawInput, env = process.env) {
  const input = normalizeInput(rawInput);
  const workspace = createWorkspace({ taskId: input.taskId, workspaceRoot: resolveWorkspaceRoot(env) });
  const uploadRoot = env.RZYX_AI_UPLOAD_ROOT || (env.RZYX_AI_DATA_DIR ? path.join(env.RZYX_AI_DATA_DIR, 'uploads') : undefined);
  const videoPath = resolveVideoPath(input.videoUrl, { uploadRoot });
  emitEvent('video', 'video path resolved', { videoPath });

  const { videoInfo, frames } = await extractFrames({
    workspace,
    videoPath,
    fps: input.fps,
    quality: input.quality,
  });

  if (input.mode === 'frames-only') {
    const output = buildFinalOutput({
      workspace,
      videoInfo,
      vehicleInfo: {},
      candidates: [],
      damages: [],
      bestFrameImages: [],
      reviewImages: [],
      uncertainDamages: [],
      artifacts: { videoInfo: 'video_info.json' },
    });
    writeJson(workspace, 'run_summary.json', output);
    emitEvent('done', 'frames-only inspection complete', { frameCount: frames.length });
    return output;
  }

  const modelConfig = resolveModelConfig(env);
  const detection = await detectDamageCandidates({
    workspace,
    frames,
    modelConfig,
    prompt: readPrompt('damage_detect.md'),
    batchSize: input.batchSize,
    concurrency: input.concurrency,
  });

  if (input.mode === 'detect-only' || detection.candidates.length === 0) {
    const output = buildFinalOutput({
      workspace,
      videoInfo,
      vehicleInfo: detection.vehicleInfo,
      candidates: detection.candidates,
      damages: [],
      bestFrameImages: [],
      reviewImages: [],
      uncertainDamages: [],
      artifacts: { videoInfo: 'video_info.json', damageCandidates: 'damage_candidates.json', runSummary: 'run_summary.json' },
    });
    writeJson(workspace, 'run_summary.json', output);
    emitEvent('done', detection.candidates.length === 0 ? 'no candidates found' : 'detect-only inspection complete', { damageCount: 0 });
    return output;
  }

  const grounding = await groundDamages({
    workspace,
    frames,
    candidates: detection.candidates,
    modelConfig,
    prompt: readPrompt('grounding.md'),
    groundingWindow: input.groundingWindow,
    frameLimit: input.groundingFrameLimit,
    concurrency: Math.min(3, input.concurrency),
  });

  const reviewed = await reviewDamageAnnotations({
    workspace,
    annotations: grounding.annotations,
    modelConfig,
    prompt: readPrompt('damage_review.md'),
    concurrency: input.reviewConcurrency,
  });

  const best = await selectBestFrames({
    workspace,
    annotations: reviewed.accepted,
    topN: input.topN,
    modelConfig,
    prompt: readPrompt('best_frame.md'),
  });

  const output = buildFinalOutput({
    workspace,
    videoInfo,
    vehicleInfo: detection.vehicleInfo,
    candidates: detection.candidates,
    damages: best.damages,
    bestFrameImages: best.bestFrameImages,
    reviewImages: reviewed.reviewImages,
    uncertainDamages: reviewed.uncertain.map(item => ({
      id: item.damageId,
      part: item.part,
      type: item.type,
      severity: item.severity,
      description: item.review?.reason || item.description,
      timestamps: (item.markedFrames || []).map(frame => frame.timestamp),
      review: item.review || null,
    })),
    artifacts: {
      videoInfo: 'video_info.json',
      damageCandidates: 'damage_candidates.json',
      damageAnnotations: 'damage_annotations.json',
      damageReview: 'damage_review.json',
      bestFrames: 'best_frames.json',
      runSummary: 'run_summary.json',
    },
  });
  writeJson(workspace, 'run_summary.json', output);
  emitEvent('done', 'full inspection complete', { damageCount: output.damages.length });
  return output;
}

async function main() {
  try {
    const stdin = fs.readFileSync(0, 'utf8');
    const input = JSON.parse(stdin || '{}');
    const output = await run(input);
    process.stdout.write(JSON.stringify(output));
  } catch (error) {
    process.stdout.write(JSON.stringify({ success: false, error: error.message }));
    process.exitCode = 0;
  }
}

if (require.main === module) {
  main();
}

module.exports = {
  normalizeInput,
  buildFinalOutput,
  run,
};
