import pytest

from hef.catalog import CatalogError, load_catalog, validate_catalog
from tools.drafting import HeuristicProposer
from tools.ingest.base import Candidate, ingest_candidate, ingest_search
from tools.probe import Probe


def make_candidate(**o):
    base = dict(
        source_archive="nasa",
        source_url="https://example.org/landing/x",
        media_url="https://example.org/media/x.mp4",
        title="Earthrise",
        license="public_domain",
        attribution="",
        suggested_id="nasa-earthrise",
        media_ext="mp4",
        description="a view of earth",
    )
    base.update(o)
    return Candidate(**base)


def video_probe():
    return Probe(
        streams=[
            {
                "codec_type": "video",
                "width": 1920,
                "height": 1080,
                "disposition": {"attached_pic": 0},
            },
            {"codec_type": "audio"},
        ],
        format={"duration": "10.0"},
    )


def make_prober(probe):
    def prober(path):
        return probe

    return prober


def make_downloader(content=b"FAKEMEDIA"):
    def downloader(url, dest):
        dest.write_bytes(content)

    return downloader


def test_ingest_appends_one_proposed_record(tmp_path):
    catalog = tmp_path / "library.jsonl"
    media_root = tmp_path / "media"
    rec = ingest_candidate(
        make_candidate(),
        catalog_path=catalog,
        media_root=media_root,
        proposer=HeuristicProposer(),
        prober=make_prober(video_probe()),
        downloader=make_downloader(),
    )
    assert rec is not None
    records = load_catalog(catalog)
    assert len(records) == 1
    r = records[0]
    assert r.id == "nasa-earthrise"
    assert r.mode == "av"
    assert r.resolution == "1920x1080"
    assert r.duration_s == 10
    assert r.review_status == "proposed"
    assert r.reviewed_at is None
    assert r.rationale != ""
    assert r.file_path == "nasa/nasa-earthrise.mp4"
    assert r.notes == "a view of earth"
    # media written under media_root/<archive>/<id>.<ext>
    assert (media_root / "nasa" / "nasa-earthrise.mp4").exists()


def test_ingest_is_idempotent(tmp_path):
    catalog = tmp_path / "library.jsonl"
    media_root = tmp_path / "media"
    kw = dict(
        catalog_path=catalog,
        media_root=media_root,
        proposer=HeuristicProposer(),
        prober=make_prober(video_probe()),
        downloader=make_downloader(),
    )
    first = ingest_candidate(make_candidate(), **kw)
    second = ingest_candidate(make_candidate(), **kw)
    assert first is not None
    assert second is None  # skipped as duplicate
    assert len(load_catalog(catalog)) == 1


def test_dominant_color_off_by_default(tmp_path):
    catalog = tmp_path / "library.jsonl"
    rec = ingest_candidate(
        make_candidate(),
        catalog_path=catalog,
        media_root=tmp_path / "media",
        proposer=HeuristicProposer(),
        prober=make_prober(video_probe()),
        downloader=make_downloader(),
    )
    assert rec.dominant_color == ""


def test_dominant_color_computed_when_enabled(tmp_path):
    catalog = tmp_path / "library.jsonl"
    color_calls = []

    def fake_color_fn(path, *, midpoint_s=0.0):
        color_calls.append(midpoint_s)
        return "#abcdef"

    rec = ingest_candidate(
        make_candidate(),
        catalog_path=catalog,
        media_root=tmp_path / "media",
        proposer=HeuristicProposer(),
        prober=make_prober(video_probe()),
        downloader=make_downloader(),
        compute_color=True,
        color_fn=fake_color_fn,
    )
    assert rec.dominant_color == "#abcdef"
    assert color_calls  # was invoked


def test_audio_keeps_empty_color_even_when_enabled(tmp_path):
    audio = Probe(streams=[{"codec_type": "audio"}], format={"duration": "30.0"})

    def boom(path, *, midpoint_s=0.0):
        raise AssertionError("must not compute color for audio")

    rec = ingest_candidate(
        make_candidate(media_ext="mp3", source_archive="librivox", license="public_domain"),
        catalog_path=tmp_path / "library.jsonl",
        media_root=tmp_path / "media",
        proposer=HeuristicProposer(),
        prober=make_prober(audio),
        downloader=make_downloader(),
        compute_color=True,
        color_fn=boom,
    )
    assert rec.mode == "audio" and rec.dominant_color == ""


def test_unmappable_license_raises_and_writes_nothing(tmp_path):
    catalog = tmp_path / "library.jsonl"
    with pytest.raises(CatalogError):
        ingest_candidate(
            make_candidate(license="all_rights_reserved"),
            catalog_path=catalog,
            media_root=tmp_path / "media",
            proposer=HeuristicProposer(),
            prober=make_prober(video_probe()),
            downloader=make_downloader(),
        )
    assert not catalog.exists() or load_catalog(catalog) == []


def test_ingest_search_runs_over_hits(tmp_path):
    catalog = tmp_path / "library.jsonl"

    class FakeFetcher:
        archive = "nasa"

        def search(self, query, *, limit):
            return [
                make_candidate(suggested_id="nasa-a", media_url="u/a.mp4"),
                make_candidate(suggested_id="nasa-b", media_url="u/b.mp4"),
            ][:limit]

        def resolve(self, identifier):  # pragma: no cover - not used here
            raise NotImplementedError

    recs = ingest_search(
        FakeFetcher(),
        "earth",
        limit=2,
        catalog_path=catalog,
        media_root=tmp_path / "media",
        proposer=HeuristicProposer(),
        prober=make_prober(video_probe()),
        downloader=make_downloader(),
    )
    assert [r.id for r in recs] == ["nasa-a", "nasa-b"]
    validate_catalog(load_catalog(catalog))
