"""Map file I/O utilities for loading URL maps.

This module provides functionality to load map files (JSON or JSONL)
generated by the map command and extract URLs for crawling.
"""

import json
import logging
from pathlib import Path
from typing import Any

from supacrawl.utils import normalise_url

LOGGER = logging.getLogger(__name__)


def load_map_entries(map_path: Path) -> list[dict[str, Any]]:
    """
    Load map entries from a file (JSON or JSONL).

    Args:
        map_path: Path to map file (JSON array or JSONL).

    Returns:
        List of map entry dictionaries.

    Raises:
        ValueError: If file format is invalid.
        FileNotFoundError: If file does not exist.
    """
    if not map_path.exists():
        raise FileNotFoundError(f"Map file not found: {map_path}")

    content = map_path.read_text(encoding="utf-8").strip()

    if not content:
        return []

    # Try JSON array first
    try:
        parsed_entries = json.loads(content)
        if isinstance(parsed_entries, list):
            return parsed_entries
    except json.JSONDecodeError:
        pass

    # Try JSONL (one JSON object per line)
    entries: list[dict[str, Any]] = []
    for line in content.splitlines():
        line = line.strip()
        if not line:
            continue
        try:
            entry = json.loads(line)
            if isinstance(entry, dict):
                entries.append(entry)
        except json.JSONDecodeError as e:
            LOGGER.warning("Skipping invalid JSONL line in %s: %s", map_path, e)
            continue

    if entries:
        return entries

    raise ValueError(f"Invalid map file format: {map_path}. Expected JSON array or JSONL.")


def select_crawl_urls(map_entries: list[dict[str, Any]]) -> list[str]:
    """
    Select URLs from map entries for crawling.

    Default filter includes only entries where:
    - included == true (if present), and
    - allowed == true (if present)

    If those keys are missing, treat the entry as eligible.

    Args:
        map_entries: List of map entry dictionaries.

    Returns:
        Sorted list of normalised URLs (lexicographic order for determinism).
    """
    urls: list[str] = []
    seen: set[str] = set()

    for entry in map_entries:
        if not isinstance(entry, dict):
            continue

        url = entry.get("url")
        if not url or not isinstance(url, str):
            continue

        # Default filter: include only if allowed and included (if keys present)
        # If keys missing, treat as eligible
        allowed = entry.get("allowed")
        included = entry.get("included")

        # If keys are present, they must be True
        if allowed is not None and not allowed:
            continue
        if included is not None and not included:
            continue

        # Normalise and deduplicate
        normalised = normalise_url(url, html=None, entrypoint=None)
        if normalised and normalised not in seen:
            seen.add(normalised)
            urls.append(normalised)

    # Sort for determinism
    return sorted(urls)
