"""Give each orphaned figure back to the question it actually belongs to.

The candidates come from search over the figure's own description; the decision
comes from looking at the figure against the candidate's stem, which is the
same audit that detached it, asked the other way round. Only a clear "yes"
attaches, and only to a question that has no figure of its own — replacing one
picture with another on the strength of a guess is how this mess started.
"""
import json

from app.database import SessionLocal
from app.models.media import MediaAsset
from app.models.question import Question
from app.models.question_media import QuestionMedia
from app.services import storage_service, thumbnails, vision_service
from app.services.ai_service import chat
from app.services.search_service import hybrid_ids
from app.tasks.quiz_tasks import FIGURE_AUDIT_MODEL, FIGURE_AUDIT_PROMPT

ORPHANS = [1120, 1143, 1179, 1194, 1196, 1204, 1206, 1235, 1843, 2511, 2514, 2813, 3029, 3638]
report = json.load(open("/tmp/figures.log"))
by_id = {m["question_id"]: m for m in report["mismatches"]}

db = SessionLocal()
placed, homeless = [], []

for question_id in ORPHANS:
    entry = by_id.get(question_id)
    if not entry:
        continue
    asset = db.query(MediaAsset).filter(MediaAsset.path == entry["path"]).first()
    described = (asset.caption or asset.alt_text or entry["shows"]) if asset else entry["shows"]
    data = storage_service.load(entry["path"])
    if not data:
        homeless.append((question_id, "the file could not be read"))
        continue
    image = vision_service.prepare(thumbnails.render(data, 640) or data, "image/webp")
    if image is None:
        homeless.append((question_id, "the image could not be prepared"))
        continue

    ranked, _ = hybrid_ids(db, described, "question", limit=8)
    home = None
    for candidate_id in ranked:
        candidate = db.get(Question, candidate_id)
        if not candidate or candidate.image_path:
            continue
        try:
            raw = (chat(model=FIGURE_AUDIT_MODEL, max_tokens=300, temperature=0, messages=[{
                "role": "user", "content": [
                    vision_service.image_part(image),
                    {"type": "text", "text": FIGURE_AUDIT_PROMPT + (candidate.question_text or "")[:900]},
                ]}]) or "").strip()
            if raw.startswith("```"):
                raw = raw.split("\n", 1)[1] if "\n" in raw else raw[3:]
                raw = raw[:-3] if raw.endswith("```") else raw
            verdict = json.loads(raw.strip())
        except Exception as exc:
            print(f"  Q#{candidate_id}: could not ask ({exc})")
            continue
        if str(verdict.get("belongs", "")).lower() == "yes":
            home = candidate
            break

    if home is None:
        homeless.append((question_id, "no question claimed it"))
        continue

    home.image_path = entry["path"]
    if asset and not db.query(QuestionMedia).filter_by(
            question_id=home.id, media_id=asset.id).first():
        db.add(QuestionMedia(question_id=home.id, media_id=asset.id))
    db.commit()
    placed.append((question_id, home.id, described[:70], (home.question_text or "")[:70]))

print("\n=== placed ===")
for was, now, figure, stem in placed:
    print(f"  {figure}\n    was on #{was} → now on #{now}: {stem}")
print("\n=== still homeless ===")
for question_id, why in homeless:
    print(f"  the figure from #{question_id}: {why}")
