// AutoEQSL drives automatic eQSL sending. The backend decides eligibility on
// log (auto-send on, recipient e-mail known, default template, not already
// sent) and emits "qsl:autosend"; this component does the part Go can't —
// render the card off-screen, rasterize it to JPEG and hand it back to
// SendEQSL. Jobs are processed one at a time. Mounted once, near the app root.

import { useCallback, useEffect, useRef, useState } from 'react';
import { EventsOn } from '../../../wailsjs/runtime/runtime';
import { RenderEQSL, SendEQSL } from '../../../wailsjs/go/main/App';
import type { RenderModel } from './qslTypes';
import { loadCardAssets, type CardAssets } from './qslAssets';
import { CardPreview } from './CardPreview';
import { rasterizeCard } from './rasterize';

interface Job {
  qsoId: number;
  templateId: number;
  callsign: string;
}

interface Props {
  onSent?: (callsign: string) => void;
  onError?: (message: string) => void;
}

export function AutoEQSL({ onSent, onError }: Props) {
  const queue = useRef<Job[]>([]);
  const busy = useRef(false);
  const [current, setCurrent] = useState<{ job: Job; model: RenderModel; assets: CardAssets } | null>(null);
  const svgEl = useRef<SVGSVGElement | null>(null);
  const sentForRef = useRef<number | null>(null); // qsoId we've already fired SendEQSL for

  // Keep the callbacks in refs so they never change the effects' identity — a
  // toast/grid re-render from onSent must NOT re-run the send effect (that
  // re-sent the same eQSL many times in a row).
  const onSentRef = useRef(onSent);
  const onErrorRef = useRef(onError);
  useEffect(() => { onSentRef.current = onSent; onErrorRef.current = onError; });

  // Pull the next job, fetch its render model + assets, then mount it (the
  // effect below rasterizes once the DOM has it).
  const pump = useCallback(async () => {
    if (busy.current) return;
    const job = queue.current.shift();
    if (!job) return;
    busy.current = true;
    try {
      const model = JSON.parse(await RenderEQSL(job.qsoId, job.templateId)) as RenderModel;
      const assets = await loadCardAssets(model.template, job.templateId);
      setCurrent({ job, model, assets });
    } catch (e) {
      onErrorRef.current?.(`Auto eQSL: ${e}`);
      busy.current = false;
      void pump();
    }
  }, []);

  useEffect(() => {
    const off = EventsOn('qsl:autosend', (p: { qsoId: number; templateId: number; callsign: string }) => {
      // Dedupe: ignore a repeat event for a QSO we're already handling/handled.
      if (sentForRef.current === p.qsoId || queue.current.some((j) => j.qsoId === p.qsoId)) return;
      queue.current.push({ qsoId: p.qsoId, templateId: p.templateId, callsign: p.callsign });
      void pump();
    });
    return () => off();
  }, [pump]);

  // Once a job is mounted off-screen, wait for fonts + paint, rasterize, send.
  // Sends exactly once per job (guarded by sentForRef), independent of renders.
  useEffect(() => {
    if (!current) return;
    if (sentForRef.current === current.job.qsoId) return; // already sent this one
    let cancelled = false;
    void (async () => {
      try {
        await document.fonts.ready;
        await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(() => r(null))));
        if (cancelled || !svgEl.current) return;
        sentForRef.current = current.job.qsoId;
        const card = current.model.template.card;
        const jpeg = await rasterizeCard(svgEl.current, card.w, card.h, 'image/jpeg');
        await SendEQSL(current.job.qsoId, current.job.templateId, jpeg);
        onSentRef.current?.(current.job.callsign);
      } catch (e) {
        onErrorRef.current?.(`Auto eQSL: ${e}`);
      } finally {
        if (!cancelled) {
          setCurrent(null);
          busy.current = false;
          void pump();
        }
      }
    })();
    return () => { cancelled = true; };
  }, [current, pump]);

  if (!current) return null;
  // Off-screen at full card resolution so the rasterized output matches the
  // editor preview exactly.
  return (
    <div
      aria-hidden
      style={{ position: 'fixed', left: -100000, top: 0, opacity: 0, pointerEvents: 'none' }}
    >
      <CardPreview
        model={current.model}
        assets={current.assets}
        width={current.model.template.card.w}
        svgRef={(el) => { svgEl.current = el; }}
      />
    </div>
  );
}
