"""Bounded FLV metadata reader for native TC260 AIGC labels.

TC260-PG-20257A stores the label in the ``onMetaData`` script tag as an AMF0
property named ``AIGC`` whose string value is the normative JSON object. Media
tag payloads are skipped without being loaded.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from pathlib import Path

from remove_ai_watermarks.metadata import MAX_TC260_VALUE_BYTES, parse_tc260_aigc_json

_SCRIPT_TAG = 18
_MAX_SCRIPT_BYTES = 4 * 1024 * 1024


def _u24(value: bytes) -> int:
    return int.from_bytes(value, "big")


def _amf0_string(data: bytes, position: int, *, long: bool = False) -> tuple[bytes, int] | None:
    length_size = 4 if long else 2
    if position + length_size > len(data):
        return None
    length = int.from_bytes(data[position : position + length_size], "big")
    start = position + length_size
    end = start + length
    if end > len(data):
        return None
    return data[start:end], end


def _skip_amf0(data: bytes, position: int, depth: int = 0) -> int | None:
    if position >= len(data) or depth > 8:
        return None
    value_type = data[position]
    position += 1
    if value_type == 0:
        return position + 8 if position + 8 <= len(data) else None
    if value_type == 1:
        return position + 1 if position + 1 <= len(data) else None
    if value_type == 2:
        parsed = _amf0_string(data, position)
        return parsed[1] if parsed is not None else None
    if value_type in {5, 6}:
        return position
    if value_type == 7:
        return position + 2 if position + 2 <= len(data) else None
    if value_type == 11:
        return position + 10 if position + 10 <= len(data) else None
    if value_type == 12:
        parsed = _amf0_string(data, position, long=True)
        return parsed[1] if parsed is not None else None
    if value_type == 10:
        if position + 4 > len(data):
            return None
        count = int.from_bytes(data[position : position + 4], "big")
        position += 4
        for _ in range(count):
            next_position = _skip_amf0(data, position, depth + 1)
            if next_position is None:
                return None
            position = next_position
        return position
    if value_type in {3, 8}:
        if value_type == 8:
            if position + 4 > len(data):
                return None
            position += 4
        while position + 3 <= len(data):
            name_length = int.from_bytes(data[position : position + 2], "big")
            position += 2
            if name_length == 0 and data[position] == 9:
                return position + 1
            position += name_length
            if position > len(data):
                return None
            next_position = _skip_amf0(data, position, depth + 1)
            if next_position is None:
                return None
            position = next_position
        return None
    return None


def _script_payloads(data: bytes) -> tuple[bytes, ...]:
    first = _amf0_string(data, 1) if data[:1] == b"\x02" else None
    if first is None or first[0] != b"onMetaData":
        return ()
    position = first[1]
    if position >= len(data) or data[position] not in {3, 8}:
        return ()
    if data[position] == 8:
        position += 5
    else:
        position += 1
    found: list[bytes] = []
    while position + 3 <= len(data):
        name_length = int.from_bytes(data[position : position + 2], "big")
        position += 2
        if name_length == 0 and data[position] == 9:
            break
        name_end = position + name_length
        if name_end > len(data):
            break
        name = data[position:name_end]
        position = name_end
        if name == b"AIGC" and position < len(data) and data[position] in {2, 12}:
            long = data[position] == 12
            parsed = _amf0_string(data, position + 1, long=long)
            if parsed is None:
                break
            value, position = parsed
            if len(value) <= MAX_TC260_VALUE_BYTES and parse_tc260_aigc_json(value) is not None:
                found.append(value)
            continue
        next_position = _skip_amf0(data, position)
        if next_position is None:
            break
        position = next_position
    return tuple(found)


def tc260_aigc_payloads(path: str | Path) -> tuple[bytes, ...]:
    """Read validated TC260 values from FLV ``script.onMetaData.AIGC``."""
    found: list[bytes] = []
    try:
        with open(path, "rb") as stream:
            header = stream.read(9)
            if len(header) != 9 or header[:3] != b"FLV":
                return ()
            data_offset = int.from_bytes(header[5:9], "big")
            stream.seek(0, 2)
            file_size = stream.tell()
            position = data_offset + 4
            while position + 11 <= file_size:
                stream.seek(position)
                tag_header = stream.read(11)
                if len(tag_header) != 11:
                    break
                tag_type = tag_header[0] & 0x1F
                data_size = _u24(tag_header[1:4])
                payload_start = position + 11
                payload_end = payload_start + data_size
                if payload_end + 4 > file_size:
                    break
                if tag_type == _SCRIPT_TAG and data_size <= _MAX_SCRIPT_BYTES:
                    payload = stream.read(data_size)
                    if len(payload) == data_size:
                        found.extend(_script_payloads(payload))
                        if found:
                            return tuple(found)
                position = payload_end + 4
    except OSError:
        return ()
    return tuple(found)
