"""Types for extracting data from JSON responses."""

import dataclasses
from typing import NewType

from mariner.models.html import FieldFilter

Selector = NewType("Selector", str)


@dataclasses.dataclass(frozen=True)
class Field:
    """Specification to extract a value from a JSON document.

    Args:
        selector: A key name for direct lookup in a JSON row dict.
        text: A Go-style template expression for computed fields.
        optional: Whether the field can be missing without error.
        default: Default value when the field is missing.
        filters: Filter chain applied after extraction.

    """

    selector: Selector | None = None
    text: str | None = None
    optional: bool = False
    default: str | None = None
    filters: list[FieldFilter] = dataclasses.field(default_factory=list)


@dataclasses.dataclass(frozen=True)
class SearchFields:
    """Search field definitions for a JSON-based tracker.

    Well-known torrent fields are explicit attributes. Intermediate
    fields used only for template interpolation live in ``extras``.
    """

    title: Field
    size: Field
    date: Field
    seeders: Field
    leechers: Field
    magnet: Field | None = None
    infohash: Field | None = None
    download: Field | None = None
    extras: dict[str, Field] = dataclasses.field(default_factory=dict)

    @property
    def all_fields(self) -> dict[str, Field]:
        """Return all fields (known + extras) as a flat dict.

        The two-pass extraction needs to see every field together
        so that template expressions can reference intermediaries.
        """
        fields: dict[str, Field] = {}
        for f in dataclasses.fields(self):
            if f.name == "extras":
                continue
            value = getattr(self, f.name)
            if value is not None:
                fields[f.name] = value
        fields.update(self.extras)
        return fields
