const fs = require('node:fs');
const path = require('node:path');
const { parseJsonLoose } = require('./json_utils.cjs');
const { callVisionApi } = require('./vision_client.cjs');
const { emitEvent, writeJson, publicUrl } = require('./workspace.cjs');

function heuristicFrameScore(frame) {
  let score = 0;
  if (frame.bbox && frame.bbox.normalized) {
    score += 100;
    const box = frame.bbox.normalized;
    const area = Math.max(0, box.x2 - box.x1) * Math.max(0, box.y2 - box.y1);
    if (area >= 1500 && area <= 180000) score += 35;
    if (box.x1 > 5 && box.y1 > 5 && box.x2 < 995 && box.y2 < 995) score += 20;
  }
  return score;
}

function buildBestFramePrompt(annotation, prompt) {
  const frames = (annotation.markedFrames || []).map(frame => ({
    timestamp: frame.timestamp,
    hasBbox: Boolean(frame.bbox),
    bbox: frame.bbox?.normalized || null,
  }));
  return `${prompt}\n损伤ID：${annotation.damageId}\n部位：${annotation.part}\n类型：${annotation.type}\n描述：${annotation.description}\n候选帧：${JSON.stringify(frames)}`;
}

async function chooseFrames({ annotation, topN, modelConfig, prompt }) {
  const frames = (annotation.markedFrames || []).slice();
  if (frames.length <= 1) return frames;
  const result = await callVisionApi({
    config: modelConfig,
    model: modelConfig.bestFrameModel,
    messages: [{ role: 'user', content: [{ type: 'text', text: buildBestFramePrompt(annotation, prompt) }] }],
    maxTokens: 1024,
    temperature: 0.1,
  });
  if (result.ok) {
    const parsed = parseJsonLoose(result.content);
    const timestamps = Array.isArray(parsed?.best_timestamps) ? parsed.best_timestamps.map(Number) : [];
    const selected = [];
    for (const ts of timestamps) {
      const frame = frames.find(item => Math.abs(Number(item.timestamp) - ts) < 0.02);
      if (frame && !selected.includes(frame)) selected.push(frame);
      if (selected.length >= topN) break;
    }
    if (selected.length > 0) return selected;
  }
  return frames.sort((a, b) => heuristicFrameScore(b) - heuristicFrameScore(a)).slice(0, topN);
}

async function copyBestFrame(workspace, damageId, frame, order) {
  const name = `best_${String(order).padStart(2, '0')}_${damageId}_${frame.timestamp.toFixed(2).replace('.', '_')}s.jpg`;
  const targetPath = path.join(workspace.workspacePath, 'source', name);
  fs.copyFileSync(frame.path, targetPath);
  return {
    timestamp: frame.timestamp,
    path: targetPath,
    relativePath: `source/${name}`,
    url: publicUrl(workspace, `source/${name}`),
    mimeType: 'image/jpeg',
    label: `最佳证据帧 ${damageId}`,
    purpose: 'best_evidence_frame',
    damageId,
    bbox: frame.bbox?.normalized || null,
  };
}

async function selectBestFrames({ workspace, annotations, topN = 1, modelConfig, prompt }) {
  emitEvent('best-frame', 'selecting best frames', { annotationCount: annotations.length, topN, model: modelConfig.bestFrameModel });
  const damages = [];
  const bestFrameImages = [];
  for (let index = 0; index < annotations.length; index += 1) {
    const annotation = annotations[index];
    const selected = await chooseFrames({ annotation, topN, modelConfig, prompt });
    const damageId = annotation.damageId || `damage_${String(index + 1).padStart(3, '0')}`;
    const images = [];
    for (const frame of selected) {
      const image = await copyBestFrame(workspace, damageId, frame, bestFrameImages.length + 1);
      images.push(image);
      bestFrameImages.push(image);
    }
    damages.push({
      id: damageId,
      part: annotation.part,
      type: annotation.type,
      severity: annotation.severity,
      confidence: annotation.review?.confidence ?? annotation.confidence ?? null,
      description: annotation.review?.reason || annotation.description,
      timestamps: (annotation.markedFrames || []).map(frame => frame.timestamp),
      bestFrames: images.map(image => ({
        timestamp: image.timestamp,
        path: image.path,
        relativePath: image.relativePath,
        url: image.url,
        bbox: image.bbox,
      })),
      review: annotation.review || null,
    });
  }
  const output = { damageCount: damages.length, bestFrameCount: bestFrameImages.length, damages, bestFrameImages };
  writeJson(workspace, 'best_frames.json', output);
  emitEvent('best-frame', 'best frame selection complete', { damageCount: damages.length });
  return output;
}

module.exports = {
  heuristicFrameScore,
  buildBestFramePrompt,
  selectBestFrames,
};
