"""Sync profile photos from CSIS public API to User.avatar.

Match strategy: tokenize scholar name, match against (first_name + last_name) on User
where Employee exists. Best-token-overlap wins.

Usage:
    uv run python manage.py sync_csis_photos [--dry-run]
"""

import json
import re
import ssl
import urllib.request
from pathlib import Path

from django.contrib.auth import get_user_model
from django.core.files.base import ContentFile
from django.core.management.base import BaseCommand

from apps.hr.models import Employee

User = get_user_model()
CSIS_API = "https://api.csis.or.id/api/persons/scholars/"


def tokens(name: str) -> set:
    """Lowercase alpha tokens, drop single-letter initials and very short tokens."""
    if not name:
        return set()
    cleaned = re.sub(r"[^a-zA-Z\s]", " ", name).lower()
    return {t for t in cleaned.split() if len(t) >= 3}


def best_match(scholar_name: str, candidates):
    """candidates: list of (user, name, email_local_tokens)"""
    s_tokens = tokens(scholar_name)
    if not s_tokens:
        return None, 0
    best_user = None
    best_score = 0
    for user, name, email_tokens in candidates:
        c_tokens = tokens(name) | email_tokens
        score = len(s_tokens & c_tokens)
        if score > best_score:
            best_score = score
            best_user = user
    return best_user, best_score


class Command(BaseCommand):
    help = "Fetch CSIS scholar photos and attach to matching User.avatar"

    def add_arguments(self, parser):
        parser.add_argument("--dry-run", action="store_true")
        parser.add_argument("--min-score", type=int, default=2,
                            help="Minimum matched tokens to accept (default 2)")

    def handle(self, *args, **options):
        ctx = ssl.create_default_context()
        ctx.check_hostname = False
        ctx.verify_mode = ssl.CERT_NONE

        self.stdout.write("Fetching scholars from CSIS API...")
        try:
            with urllib.request.urlopen(CSIS_API, timeout=15, context=ctx) as resp:
                scholars = json.loads(resp.read().decode())
        except Exception as e:
            self.stderr.write(self.style.ERROR(f"Failed to fetch API: {e}"))
            return

        self.stdout.write(f"Fetched {len(scholars)} scholars")

        # Build candidate list once: only users that have Employee record
        candidates = []
        for emp in Employee.objects.select_related("user").all():
            u = emp.user
            full = f"{u.first_name} {u.last_name}".strip() or u.email
            local = (u.email or "").split("@")[0]
            email_toks = tokens(local.replace(".", " ").replace("_", " ").replace("-", " "))
            candidates.append((u, full, email_toks))

        self.stdout.write(f"Employee candidates: {len(candidates)}")

        synced = 0
        no_match = 0
        no_image = 0
        download_fail = 0

        for scholar in scholars:
            name = scholar.get("name", "")
            img_url = scholar.get("profile_img", "")

            if not img_url or "avatar.png" in img_url:
                no_image += 1
                continue

            user, score = best_match(name, candidates)
            if not user or score < options["min_score"]:
                no_match += 1
                self.stdout.write(f"  no match: {name} (best score={score})")
                continue

            user_full = f"{user.first_name} {user.last_name}".strip()
            self.stdout.write(f"  match: '{name}' -> '{user_full}' [{user.email}] score={score}")

            if options["dry_run"]:
                continue

            try:
                with urllib.request.urlopen(img_url, timeout=15, context=ctx) as resp:
                    data = resp.read()
            except Exception as e:
                self.stderr.write(f"  download failed: {name} - {e}")
                download_fail += 1
                continue

            filename = Path(img_url).name or f"{user.username}.png"
            user.avatar.save(filename, ContentFile(data), save=True)

            # Mirror to Contact.avatar if Contact exists for this user
            from apps.contacts.models import Contact
            contact = Contact.objects.filter(user=user).first()
            if contact and not contact.avatar:
                contact.avatar.save(filename, ContentFile(data), save=True)

            synced += 1

        verb = "Would sync" if options["dry_run"] else "Synced"
        self.stdout.write(self.style.SUCCESS(
            f"\n{verb} {synced}, no match: {no_match}, no image: {no_image}, download failed: {download_fail}"
        ))
