"""Inbox helpers for the AuditWalk MVP.

Stores an inbox of URL items in a JSONL file (one JSON object per line),
and can snapshot a URL's HTML into a pages directory.
"""

from __future__ import annotations

import json
import os
import time
from dataclasses import asdict, dataclass
from pathlib import Path
from typing import Iterator, Optional


DEFAULT_INBOX_PATH = Path.home() / ".auditwalk" / "inbox.jsonl"
DEFAULT_PAGES_DIR = Path.home() / ".auditwalk" / "pages"

INBOX_PATH = Path(os.environ.get("AUDITWALK_INBOX", str(DEFAULT_INBOX_PATH)))
PAGES_DIR = Path(os.environ.get("AUDITWALK_PAGES_DIR", str(DEFAULT_PAGES_DIR)))


@dataclass(slots=True)
class InboxItem:
    url: str
    title: str
    timestamp: float
    source: str = "bookmarklet"
    note: Optional[str] = None

    def to_json(self) -> str:
        return json.dumps(asdict(self), separators=(",", ":"), ensure_ascii=False)


def ensure_inbox(path: Path | None = None) -> Path:
    """Ensure the inbox file exists and return its Path."""
    target = Path(path) if path else INBOX_PATH
    target.parent.mkdir(parents=True, exist_ok=True)
    if not target.exists():
        target.touch()
    return target


def append_item(item: InboxItem | dict, path: Path | None = None) -> InboxItem:
    """Append an item to the inbox file and return the normalized InboxItem."""
    target = ensure_inbox(path)

    payload: InboxItem
    if isinstance(item, dict):
        payload = InboxItem(**item)  # type: ignore[arg-type]
    else:
        payload = item

    with target.open("a", encoding="utf-8") as fh:
        fh.write(payload.to_json() + "\n")

    return payload


def iter_inbox(path: Path | None = None) -> Iterator[InboxItem]:
    """Yield InboxItem entries from the inbox file (skips malformed lines)."""
    target = ensure_inbox(path)

    with target.open("r", encoding="utf-8") as fh:
        for line in fh:
            line = line.strip()
            if not line:
                continue
            try:
                data = json.loads(line)
                yield InboxItem(**data)
            except Exception:
                # MVP: skip bad lines rather than failing hard
                continue


def load_inbox(path: Path | None = None, limit: int | None = None) -> list[InboxItem]:
    """Load inbox entries into a list, optionally limited to N items."""
    items: list[InboxItem] = []
    for idx, item in enumerate(iter_inbox(path)):
        items.append(item)
        if limit is not None and (idx + 1) >= limit:
            break
    return items


def add_url(
    url: str,
    title: str | None = None,
    source: str = "bookmarklet",
    note: str | None = None,
    path: Path | None = None,
) -> InboxItem:
    """Add a URL to the inbox and return the created entry."""
    entry = InboxItem(
        url=url,
        title=(title or url),
        timestamp=time.time(),
        source=source,
        note=note,
    )
    return append_item(entry, path)


def format_item(item: InboxItem, idx: int | None = None) -> str:
    """Human-readable one-line formatting."""
    prefix = f"[{idx}] " if idx is not None else ""
    ts = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(item.timestamp))
    note_part = f" | note: {item.note}" if item.note else ""
    return f"{prefix}{ts} — {item.title} ({item.url}) via {item.source}{note_part}"


def process_entry(
    entry: InboxItem | dict,
    pages_dir: Path | str | None = None,
    *,
    timeout: int = 15,
) -> str:
    """Fetch the URL and save a snapshot HTML file under pages_dir.

    Returns the saved filepath as a string.

    Requires: requests (add to requirements.txt).
    """
    import requests

    if isinstance(entry, dict):
        url = entry.get("url")
    else:
        url = entry.url

    if not url:
        raise ValueError("Entry has no url")

    pages_path = Path(pages_dir) if pages_dir else PAGES_DIR
    pages_path.mkdir(parents=True, exist_ok=True)

    headers = {"User-Agent": "AuditWalk/0.1"}
    r = requests.get(url, timeout=timeout, headers=headers)
    r.raise_for_status()

    # milliseconds to reduce collision risk
    fname = pages_path / f"{int(time.time() * 1000)}.html"
    fname.write_text(r.text, encoding="utf-8")

    return str(fname)

