"""Cross-source matching: decide which Spotify/iTunes tracks you do NOT have
locally, using real identifiers first and normalized text as a fallback.

A "wanted" track (Spotify or iTunes) counts as present locally if either:
  - its recording MBID equals a local track's MBID  -> method "mbid" (certain)
  - its normalized artist|title equals a local one  -> method "text" (fuzzy)
Otherwise it is reported as missing.
"""
from src.db.database import session_scope
from src.db.models import (
    SOURCE_ITUNES,
    SOURCE_LOCAL,
    SOURCE_SPOTIFY,
    Track,
)

WANTED_SOURCES = (SOURCE_SPOTIFY, SOURCE_ITUNES)


def _local_index(session):
    local = session.query(Track).filter_by(source=SOURCE_LOCAL).all()
    isrcs = {t.isrc for t in local if t.isrc}
    mbids = {t.mbid for t in local if t.mbid}
    norms = {t.norm_key for t in local if t.norm_key}
    return isrcs, mbids, norms


def _classify(track, local_isrcs, local_mbids, local_norms):
    """Return (has_local: bool, method). Tiers, most certain first:
    ISRC > recording MBID > normalized text."""
    if track.isrc and track.isrc in local_isrcs:
        return True, "isrc"
    if track.mbid and track.mbid in local_mbids:
        return True, "mbid"
    if track.norm_key and track.norm_key in local_norms:
        return True, "text"
    return False, None


def _row(track, method):
    return {
        "source": track.source,
        "origin": track.origin if track.source == SOURCE_SPOTIFY else "",
        "artist": track.artist,
        "title": track.title,
        "album": track.album,
        "isrc": track.isrc,
        "mbid": track.mbid,
        "matched_by": method,
        "spotify_id": track.source_id if track.source == SOURCE_SPOTIFY else None,
        "play_count": track.play_count,
    }


def classify_all(sources=WANTED_SOURCES):
    """Return (missing_rows, present_rows) for the given wanted sources."""
    with session_scope() as session:
        local_isrcs, local_mbids, local_norms = _local_index(session)
        wanted = session.query(Track).filter(Track.source.in_(sources)).all()
        missing, present = [], []
        for track in wanted:
            has_local, method = _classify(track, local_isrcs, local_mbids, local_norms)
            (present if has_local else missing).append(_row(track, method))
    return missing, present


def missing_tracks(sources=WANTED_SOURCES):
    return classify_all(sources)[0]


def summary():
    """Per-source counts: total / present-by-mbid / present-by-text / missing,
    plus how many tracks carry an MBID at all (enrichment coverage)."""
    with session_scope() as session:
        local_isrcs, local_mbids, local_norms = _local_index(session)
        out = {}
        for source in WANTED_SOURCES:
            tracks = session.query(Track).filter_by(source=source).all()
            counts = {"total": len(tracks), "present_mbid": 0,
                      "present_text": 0, "missing": 0, "has_mbid": 0,
                      "liked": 0, "from_playlist": 0, "from_top": 0}
            for track in tracks:
                if track.mbid:
                    counts["has_mbid"] += 1
                if source == SOURCE_SPOTIFY:
                    counts["liked"] += bool(track.liked)
                    counts["from_playlist"] += bool(track.from_playlist)
                    counts["from_top"] += bool(track.from_top)
                has_local, method = _classify(track, local_isrcs, local_mbids, local_norms)
                if not has_local:
                    counts["missing"] += 1
                elif method == "text":
                    counts["present_text"] += 1
                else:  # isrc or mbid -> ID-certain
                    counts["present_mbid"] += 1
            out[source] = counts
        local_total = session.query(Track).filter_by(source=SOURCE_LOCAL).count()
        out[SOURCE_LOCAL] = {"total": local_total, "has_mbid": len(local_mbids)}
    return out
