"""JPEG dimension parsing for the /watch OG image tags (S2, item 6).

Facebook's scraper fetches og:image asynchronously on first scrape, so the card is image-less
unless og:image:width/height are declared — which needs the poster's real dimensions, read from the
JPEG header without an image library. This pins that parser."""
import struct

from app.downloads.og import _jpeg_size


def _jpeg(width: int, height: int, sof_marker: int = 0xC0) -> bytes:
    """A minimal but well-formed JPEG: SOI + a JFIF APP0 segment (which the parser must SKIP) + a
    SOFn frame header carrying the dimensions + EOI. sof_marker=0xC0 baseline, 0xC2 progressive."""
    soi = b"\xff\xd8"
    # APP0 JFIF: length 0x0010 (16) = the 2 length bytes + 14 payload bytes.
    app0 = b"\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00"
    # SOFn: length 0x0011 (17) = 2 + precision(1) + height(2) + width(2) + 1 comp * 9? Payload here is
    # precision + h + w + numComp(1) + 3 components * 3 bytes = 1+2+2+1+9 = 15, +2 length = 17.
    sof = (
        b"\xff"
        + bytes([sof_marker])
        + b"\x00\x11\x08"
        + struct.pack(">H", height)
        + struct.pack(">H", width)
        + b"\x03\x01\x22\x00\x02\x11\x01\x03\x11\x01"
    )
    return soi + app0 + sof + b"\xff\xd9"


def test_reads_baseline_jpeg_dimensions(tmp_path):
    p = tmp_path / "poster.jpg"
    p.write_bytes(_jpeg(1280, 720))
    assert _jpeg_size(p) == (1280, 720)


def test_reads_progressive_jpeg_dimensions(tmp_path):
    # ffmpeg posters can be progressive (SOF2); the width/height live in the same place.
    p = tmp_path / "poster.jpg"
    p.write_bytes(_jpeg(640, 1138, sof_marker=0xC2))
    assert _jpeg_size(p) == (640, 1138)


def test_skips_preceding_segments(tmp_path):
    # The JFIF APP0 segment sits before the SOF; a naive scan that didn't skip it by length would
    # misread. The helper builds that layout, so a correct result proves the skip.
    p = tmp_path / "poster.jpg"
    p.write_bytes(_jpeg(1920, 3413))
    assert _jpeg_size(p) == (1920, 3413)


def test_non_jpeg_returns_none(tmp_path):
    p = tmp_path / "not.jpg"
    p.write_bytes(b"\x89PNG\r\n\x1a\n" + b"\x00" * 32)
    assert _jpeg_size(p) is None


def test_truncated_before_sof_returns_none(tmp_path):
    p = tmp_path / "trunc.jpg"
    p.write_bytes(b"\xff\xd8\xff\xe0\x00\x10JFIF\x00")  # SOI + start of APP0, then nothing
    assert _jpeg_size(p) is None


def test_missing_file_returns_none(tmp_path):
    assert _jpeg_size(tmp_path / "nope.jpg") is None
