// ── Group Size Calculator ─────────────────────────────────────────────────────
// Canvas-based annotation tool. All geometry is computed client-side.
// Optional API integration when a GroupPhoto ID is provided via ?gp=<id>.

let canvas, ctx;
const imgEl = new Image();

// Annotation state
let mode   = 'idle'; // 'idle' | 'ref1' | 'ref2' | 'poa' | 'poi'
let refP1  = null, refP2 = null;
let poa    = null;
let pois   = [];
let hoverPt = null;

// API context (set when ?gp= param is present)
let gpId          = null;  // GroupPhoto id
let gpPhotoId     = null;  // Photo id (for image URL)

// ── Boot ──────────────────────────────────────────────────────────────────────

document.addEventListener('DOMContentLoaded', async () => {
  canvas = document.getElementById('annotCanvas');
  ctx    = canvas.getContext('2d');

  canvas.addEventListener('click',      onCanvasClick);
  canvas.addEventListener('mousemove',  onCanvasMove);
  canvas.addEventListener('mouseleave', () => { hoverPt = null; if (mode !== 'idle') redraw(); });

  // File upload wiring
  const dropArea  = document.getElementById('dropArea');
  const fileInput = document.getElementById('fileInput');
  dropArea.addEventListener('click',    () => fileInput.click());
  fileInput.addEventListener('change',  e  => { if (e.target.files[0]) loadFile(e.target.files[0]); });
  dropArea.addEventListener('dragover', e  => { e.preventDefault(); dropArea.classList.add('drag-over'); });
  dropArea.addEventListener('dragleave',()  => dropArea.classList.remove('drag-over'));
  dropArea.addEventListener('drop',     e  => {
    e.preventDefault(); dropArea.classList.remove('drag-over');
    if (e.dataTransfer.files[0]) loadFile(e.dataTransfer.files[0]);
  });

  // Recompute when setup inputs change
  document.getElementById('refLength').addEventListener('input', () => { redraw(); updateResults(); });
  document.getElementById('distanceM').addEventListener('input',  updateResults);
  document.getElementById('bulletDia').addEventListener('input',  () => { redraw(); updateResults(); });

  // Unit switch
  const gsUnitBtns = document.getElementById('gsDistUnitBtns');
  applyDistUnitButtons(gsUnitBtns);
  gsUnitBtns.addEventListener('click', e => {
    const btn = e.target.closest('[data-dist-unit]');
    if (!btn) return;
    setDistUnit(btn.dataset.distUnit);
    applyDistUnitButtons(gsUnitBtns);
    updateResults();
  });

  // Check for ?gp= query param
  const params = new URLSearchParams(window.location.search);
  const gpParam = params.get('gp');
  if (gpParam) {
    await loadFromApi(parseInt(gpParam));
  }

  // Show back button if we came from somewhere (history) or have a ?gp= param
  const backWrap = document.getElementById('backBtnWrap');
  if (backWrap) {
    if (document.referrer) {
      const ref = new URL(document.referrer);
      document.getElementById('backBtn').href = ref.pathname + ref.search;
      backWrap.classList.remove('d-none');
    } else if (gpParam) {
      // No referrer but linked from chrono — default back to chrono
      backWrap.classList.remove('d-none');
    }
  }
});

// ── Image loading ─────────────────────────────────────────────────────────────

function loadFile(file) {
  if (!file.type.startsWith('image/')) return;
  const reader = new FileReader();
  reader.onload = e => { imgEl.onload = onImageReady; imgEl.src = e.target.result; };
  reader.readAsDataURL(file);
}

async function loadFromApi(id) {
  try {
    const gp = await apiGet(`/photos/group-photos/${id}/`);
    gpId      = gp.id;
    gpPhotoId = gp.photo.id;

    // Prefill distance from linked shot group
    if (gp.shot_group_detail?.distance_m) {
      document.getElementById('distanceM').value = gp.shot_group_detail.distance_m;
    }

    // Show "linked" badge
    const badge = document.getElementById('linkedBadge');
    if (badge) {
      badge.textContent = gp.shot_group_detail
        ? `Linked to: ${gp.shot_group_detail.label}${gp.shot_group_detail.distance_m ? ' @ ' + gp.shot_group_detail.distance_m + ' m' : ''}`
        : 'Linked group photo';
      badge.classList.remove('d-none');
    }

    // If existing POIs, prefill them (using pixel coords)
    if (gp.points_of_impact && gp.points_of_impact.length) {
      // POIs will be mapped after image load (need canvas size)
      imgEl._existingPois = gp.points_of_impact;
    }

    imgEl.onload = onImageReady;
    imgEl.src    = `/api/photos/${gpPhotoId}/data/`;
  } catch(e) {
    showUploadError('Failed to load group photo from server.');
  }
}

function onImageReady() {
  document.getElementById('uploadSection').classList.add('d-none');
  document.getElementById('annotSection').classList.remove('d-none');
  // Reset annotations (keep any prefilled distance)
  refP1 = refP2 = poa = null; pois = [];
  resizeCanvas();
  window.addEventListener('resize', resizeCanvas);

  // Restore existing POIs from API (scaled to canvas)
  if (imgEl._existingPois && imgEl._existingPois.length) {
    const sx = canvas.width  / imgEl.naturalWidth;
    const sy = canvas.height / imgEl.naturalHeight;
    pois = imgEl._existingPois.map(p => ({ x: p.x_px * sx, y: p.y_px * sy }));
    imgEl._existingPois = null;
  }

  setMode('ref1');
}

function resizeCanvas() {
  const w = document.getElementById('canvasWrap').clientWidth;
  canvas.width  = w;
  canvas.height = Math.round(w * imgEl.naturalHeight / imgEl.naturalWidth);
  redraw();
}

function showUploadError(msg) {
  const el = document.getElementById('uploadError');
  el.textContent = msg;
  el.classList.remove('d-none');
}

// ── Mode management ───────────────────────────────────────────────────────────

const STATUS = {
  idle: '',
  ref1: '① Click the first point of your reference measurement.',
  ref2: '② Click the second point of the reference measurement.',
  poa:  '③ Click the Point of Aim — the intended centre of the target.',
  poi:  '④ Click each bullet hole to add a Point of Impact. Click Compute when done.',
};

function setMode(m) {
  mode = m;
  canvas.style.cursor = m === 'idle' ? 'default' : 'crosshair';
  document.getElementById('statusMsg').textContent = STATUS[m] || '';

  ['btnRef', 'btnPoa', 'btnPoi'].forEach(id => {
    const active =
      (id === 'btnRef' && (m === 'ref1' || m === 'ref2')) ||
      (id === 'btnPoa' && m === 'poa') ||
      (id === 'btnPoi' && m === 'poi');
    const el = document.getElementById(id);
    el.className = `btn btn-sm w-100 ${active ? 'btn-primary' : 'btn-outline-secondary'}`;
  });
}

// ── Canvas events ─────────────────────────────────────────────────────────────

function canvasPt(e) {
  const r = canvas.getBoundingClientRect();
  return {
    x: (e.clientX - r.left) * canvas.width  / r.width,
    y: (e.clientY - r.top)  * canvas.height / r.height,
  };
}

function onCanvasClick(e) {
  const pt = canvasPt(e);
  switch (mode) {
    case 'ref1': refP1 = pt; refP2 = null; setMode('ref2');  break;
    case 'ref2': refP2 = pt; setMode('idle');
                 document.getElementById('refLength').focus(); break;
    case 'poa':  poa   = pt; setMode('poi');  break;
    case 'poi':  pois.push(pt);               break;
    default:     return;
  }
  redraw();
  updateResults();
}

function onCanvasMove(e) {
  hoverPt = canvasPt(e);
  if (mode !== 'idle') redraw();
}

// ── Drawing ───────────────────────────────────────────────────────────────────

function redraw() {
  if (!canvas || !imgEl.naturalWidth) return;
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.drawImage(imgEl, 0, 0, canvas.width, canvas.height);

  const scale     = getScale(); // px/mm
  const bulletDia = parseFloat(document.getElementById('bulletDia').value) || 0;
  const poiR      = scale && bulletDia ? (bulletDia / 2) * scale : 10;

  // ── Reference line ──────────────────────────────────────────────────────────
  if (refP1) {
    const end = refP2 || (mode === 'ref2' && hoverPt) || refP1;
    ctx.save();
    ctx.strokeStyle = '#3b82f6'; ctx.lineWidth = 2; ctx.setLineDash([7, 4]);
    ctx.beginPath(); ctx.moveTo(refP1.x, refP1.y); ctx.lineTo(end.x, end.y); ctx.stroke();
    drawDot(refP1.x, refP1.y, '#3b82f6', 5);
    if (refP2) {
      drawDot(refP2.x, refP2.y, '#3b82f6', 5);
      // Length label midpoint
      const mx = (refP1.x + refP2.x) / 2, my = (refP1.y + refP2.y) / 2;
      const mm = document.getElementById('refLength').value;
      if (mm) {
        ctx.fillStyle = '#fff'; ctx.font = 'bold 11px sans-serif';
        ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
        const label = mm + ' mm';
        const tw = ctx.measureText(label).width + 6;
        ctx.fillRect(mx - tw/2, my - 8, tw, 16);
        ctx.fillStyle = '#3b82f6'; ctx.fillText(label, mx, my);
      }
    }
    ctx.restore();
  }

  // ── POA crosshair ───────────────────────────────────────────────────────────
  if (poa) drawCross(poa.x, poa.y, '#22c55e', 14);

  // ── POIs ────────────────────────────────────────────────────────────────────
  pois.forEach((p, i) => {
    ctx.save();
    ctx.strokeStyle = '#ef4444'; ctx.lineWidth = 2;
    ctx.fillStyle   = 'rgba(239,68,68,0.18)';
    const r = Math.max(poiR, 8);
    ctx.beginPath(); ctx.arc(p.x, p.y, r, 0, 2 * Math.PI); ctx.fill(); ctx.stroke();
    ctx.fillStyle = '#ef4444'; ctx.font = 'bold 11px sans-serif';
    ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
    ctx.fillText(i + 1, p.x, p.y);
    ctx.restore();
  });

  // Hover ghost in POI mode
  if (mode === 'poi' && hoverPt) {
    ctx.save(); ctx.strokeStyle = 'rgba(239,68,68,0.4)'; ctx.lineWidth = 1.5;
    ctx.setLineDash([4, 3]);
    ctx.beginPath(); ctx.arc(hoverPt.x, hoverPt.y, Math.max(poiR, 8), 0, 2 * Math.PI); ctx.stroke();
    ctx.restore();
  }

  // ── Centroid + offset line ──────────────────────────────────────────────────
  const res = compute();
  if (res && pois.length >= 1) {
    const cx = poa.x + res.offsetX_mm * scale;
    const cy = poa.y - res.offsetY_mm * scale; // Y flipped (up = positive elevation)
    ctx.save(); ctx.strokeStyle = '#f97316'; ctx.lineWidth = 2; ctx.setLineDash([5, 3]);
    ctx.beginPath(); ctx.moveTo(poa.x, poa.y); ctx.lineTo(cx, cy); ctx.stroke();
    ctx.restore();
    drawDot(cx, cy, '#f97316', 7);
  }
}

function drawDot(x, y, color, r) {
  ctx.save(); ctx.fillStyle = color;
  ctx.beginPath(); ctx.arc(x, y, r, 0, 2 * Math.PI); ctx.fill();
  ctx.restore();
}

function drawCross(x, y, color, size) {
  ctx.save(); ctx.strokeStyle = color; ctx.lineWidth = 2.5;
  ctx.beginPath();
  ctx.moveTo(x - size, y); ctx.lineTo(x + size, y);
  ctx.moveTo(x, y - size); ctx.lineTo(x, y + size);
  ctx.stroke();
  ctx.beginPath(); ctx.arc(x, y, 4, 0, 2 * Math.PI); ctx.stroke();
  ctx.restore();
}

// ── Computation ───────────────────────────────────────────────────────────────

// Returns px/mm, or null if reference is not set.
function getScale() {
  const mm = parseFloat(document.getElementById('refLength').value);
  if (!refP1 || !refP2 || !mm || mm <= 0) return null;
  const d = Math.hypot(refP2.x - refP1.x, refP2.y - refP1.y);
  return d > 2 ? d / mm : null;
}

function compute() {
  const scale = getScale();
  if (!scale || !poa || !pois.length) return null;

  // Convert POIs to mm relative to POA.
  // X: positive = right.  Y: positive = up (canvas Y is flipped).
  const pts = pois.map(p => ({
    x:  (p.x - poa.x) / scale,
    y: -(p.y - poa.y) / scale,
  }));

  const cx = pts.reduce((s, p) => s + p.x, 0) / pts.length;
  const cy = pts.reduce((s, p) => s + p.y, 0) / pts.length;

  // Extreme spread (max pairwise distance)
  let groupSizeMm = 0;
  for (let i = 0; i < pts.length; i++)
    for (let j = i + 1; j < pts.length; j++)
      groupSizeMm = Math.max(groupSizeMm, Math.hypot(pts[i].x - pts[j].x, pts[i].y - pts[j].y));

  // Mean radius from centroid
  const meanRadius = pts.reduce((s, p) => s + Math.hypot(p.x - cx, p.y - cy), 0) / pts.length;

  const dist_m = parseFloat(document.getElementById('distanceM').value) || null;
  const toMoa  = dist_m ? mm => mm / (dist_m * 0.29089) : null;

  return { groupSizeMm, meanRadius, offsetX_mm: cx, offsetY_mm: cy, toMoa, scale, dist_m, pts };
}

// ── Results rendering ─────────────────────────────────────────────────────────

// fval: format a mm value using the user's chosen distance unit.
// fn = toMoa (from compute()), distM = shooting distance in metres.
function fval(mm, toMoa, distM) {
  return fDist(mm, toMoa ? toMoa(mm) : null, distM);
}

function updateResults() {
  const res     = document.getElementById('resultsSection');
  const hint    = document.getElementById('resultsHint');
  const saveBtn = document.getElementById('saveBtn');
  const r       = compute();

  if (!r) {
    res.classList.add('d-none');
    if (saveBtn) saveBtn.classList.add('d-none');
    const msg = !refP1 || !refP2       ? 'Draw a reference line on the image.'
              : !parseFloat(document.getElementById('refLength').value) ? 'Enter the reference length in mm.'
              : !poa                    ? 'Set the Point of Aim (POA).'
              : 'Add at least one Point of Impact (POI).';
    hint.textContent = msg;
    hint.classList.remove('d-none');
    return;
  }

  hint.classList.add('d-none');
  res.classList.remove('d-none');

  const { groupSizeMm, meanRadius, offsetX_mm, offsetY_mm, toMoa, dist_m } = r;
  const absW = Math.abs(offsetX_mm);
  const absE = Math.abs(offsetY_mm);

  document.getElementById('resPOICount').textContent  = pois.length;
  document.getElementById('resGroupSize').innerHTML   = pois.length >= 2
    ? fval(groupSizeMm, toMoa, dist_m)
    : '<span class="text-muted">— (need ≥ 2 POIs)</span>';
  document.getElementById('resMeanRadius').innerHTML  = fval(meanRadius, toMoa, dist_m);

  if (absW < 0.5 && absE < 0.5) {
    document.getElementById('resOffset').innerHTML     = '<span class="text-success">On POA ✓</span>';
    document.getElementById('resCorrection').innerHTML = '<span class="text-success">None needed ✓</span>';
  } else {
    const wDir  = offsetX_mm >  0.5 ? 'right' : offsetX_mm < -0.5 ? 'left'  : null;
    const eDir  = offsetY_mm >  0.5 ? 'high'  : offsetY_mm < -0.5 ? 'low'   : null;
    const cwDir = offsetX_mm >  0.5 ? 'left'  : offsetX_mm < -0.5 ? 'right' : null;
    const ceDir = offsetY_mm >  0.5 ? 'down'  : offsetY_mm < -0.5 ? 'up'    : null;

    document.getElementById('resOffset').innerHTML =
      (wDir ? `<span class="me-3">${fval(absW, toMoa, dist_m)} <strong>${wDir}</strong></span>` : '') +
      (eDir ? `<span>${fval(absE, toMoa, dist_m)} <strong>${eDir}</strong></span>` : '');

    document.getElementById('resCorrection').innerHTML =
      (cwDir ? `<span class="me-3">${fval(absW, toMoa, dist_m)} <strong>${cwDir}</strong></span>` : '') +
      (ceDir ? `<span>${fval(absE, toMoa, dist_m)} <strong>${ceDir}</strong></span>` : '');
  }

  // Hide the MOA note when the unit isn't mm (user explicitly chose a different unit)
  const showNote = getDistUnit() === 'mm' && !dist_m;
  document.getElementById('resMoaNote').classList.toggle('d-none', !showNote);

  // Show save button only when linked to a GroupPhoto
  if (saveBtn && gpId) saveBtn.classList.remove('d-none');
}

// ── Save back to API ──────────────────────────────────────────────────────────

async function saveToApi() {
  const r = compute();
  if (!r || !gpId) return;
  const btn = document.getElementById('saveBtn');
  btn.disabled = true;

  try {
    // 1. Delete existing POIs
    const existing = await apiGet(`/photos/group-photos/${gpId}/`);
    for (const poi of (existing.points_of_impact || [])) {
      await apiFetch(`/photos/group-photos/${gpId}/points/${poi.id}/`, { method: 'DELETE' });
    }

    // 2. Post new POIs with pixel + mm coords
    const sx = imgEl.naturalWidth  / canvas.width;
    const sy = imgEl.naturalHeight / canvas.height;
    for (const [i, pt] of pois.entries()) {
      const mm = r.pts[i];
      await apiPost(`/photos/group-photos/${gpId}/points/`, {
        order:  i + 1,
        x_px:   Math.round(pt.x * sx),
        y_px:   Math.round(pt.y * sy),
        x_mm:   parseFloat(mm.x.toFixed(2)),
        y_mm:   parseFloat(mm.y.toFixed(2)),
      });
    }

    // 3. Trigger server-side group size computation
    await apiPost(`/photos/group-photos/${gpId}/compute-group-size/`, {});
    showToast('Saved & computed — results stored in the session.');
  } catch(e) {
    showToast('Save failed: ' + (e.message || 'unknown error'), 'danger');
  } finally {
    btn.disabled = false;
  }
}

// ── Control buttons (called from HTML) ────────────────────────────────────────

function btnRef()   { setMode('ref1'); }
function btnPoa()   { setMode('poa'); }
function btnPoi()   { setMode('poi'); }
function btnUndo()  { if (pois.length) { pois.pop(); redraw(); updateResults(); } }
function btnReset() {
  refP1 = refP2 = poa = null; pois = [];
  document.getElementById('refLength').value = '';
  redraw(); updateResults(); setMode('ref1');
}
function loadNewPhoto() {
  document.getElementById('uploadSection').classList.remove('d-none');
  document.getElementById('annotSection').classList.add('d-none');
  window.removeEventListener('resize', resizeCanvas);
  gpId = gpPhotoId = null;
  refP1 = refP2 = poa = null; pois = [];
}

// showToast → utils.js
