"""Reddit signal via public RSS/Atom feeds (the JSON API is blocked without OAuth; RSS is not)."""
from __future__ import annotations

import re
import time
import urllib.error
import urllib.request
import xml.etree.ElementTree as ET
from datetime import datetime, timedelta, timezone
from typing import Any, Callable

from .text import html_to_text

UA = "Mozilla/5.0 FeatureScout (+https://pypi.org/project/feature-scout/)"
ATOM = "{http://www.w3.org/2005/Atom}"
PAUSE_S = 12.0   # reddit 429s back-to-back feed requests; observed OK at ~10s+ spacing
RETRY_S = 45.0   # one retry after a 429

def fetch_feed(url: str) -> str:
    req = urllib.request.Request(url, headers={"User-Agent": UA})
    with urllib.request.urlopen(req, timeout=30) as r:
        return r.read().decode("utf-8", "replace")


def parse_atom(xml_text: str) -> list[dict[str, Any]]:
    """Entries as {title, url, author, published, text}."""
    try:
        root = ET.fromstring(xml_text)
    except ET.ParseError:
        return []
    out = []
    for e in root.iter(ATOM + "entry"):
        title = (e.findtext(ATOM + "title") or "").strip()
        link = e.find(ATOM + "link")
        url = link.get("href", "") if link is not None else ""
        author = (e.findtext(ATOM + "author/" + ATOM + "name") or "").strip()
        published = (e.findtext(ATOM + "published") or e.findtext(ATOM + "updated") or "").strip()
        content = e.findtext(ATOM + "content") or ""
        text = re.sub(r"\s+", " ", html_to_text(content)).strip()
        text = re.sub(r"\[link\]|\[comments\]|submitted by\s+/u/\S+", "", text).strip()
        out.append({"title": title, "url": url, "author": author, "published": published, "text": text})
    return out


def _recent(entry: dict[str, Any], days: int) -> bool:
    if not entry.get("published"):
        return True
    try:
        ts = datetime.fromisoformat(entry["published"].replace("Z", "+00:00"))
    except ValueError:
        return True
    return ts >= datetime.now(timezone.utc) - timedelta(days=days)


def collect(urls: list[str], *, fetch: Callable[[str], str] = fetch_feed,
            days: int = 14, per_feed: int = 25, sleep: Callable[[float], None] = time.sleep) -> tuple[list[dict[str, Any]], list[str]]:
    """Recent posts from the given feeds, plus a list of feed-level problems (never raises).

    Takes the addresses directly: which communities matter is a thing each project decides,
    not something this file can know.
    """
    seen: set[str] = set()
    posts, problems = [], []
    for i, url in enumerate(urls):
        if i:
            sleep(PAUSE_S)
        try:
            entries = parse_atom(fetch(url))
        except urllib.error.HTTPError as e:
            if e.code != 429:
                problems.append(f"{url}: {e}")
                continue
            sleep(RETRY_S)
            try:
                entries = parse_atom(fetch(url))
            except Exception as e2:  # noqa: BLE001
                problems.append(f"{url}: {e2} (after retry)")
                continue
        except Exception as e:  # noqa: BLE001 — one dead feed must not sink the scan
            problems.append(f"{url}: {e}")
            continue
        if not entries:
            problems.append(f"{url}: no entries")
        for en in entries[:per_feed]:
            if en["url"] in seen or not _recent(en, days):
                continue
            seen.add(en["url"])
            posts.append(en)
    return posts, problems


def render(posts: list[dict[str, Any]], limit_chars: int = 9000) -> str:
    lines, used = [], 0
    for p in posts:
        line = f"- {p['title']} ({p['url']})"
        if p.get("text"):
            line += f": {p['text'][:280]}"
        if used + len(line) > limit_chars:
            break
        lines.append(line); used += len(line) + 1
    return "\n".join(lines) if lines else "(no Reddit posts fetched)"
