"""Async scraper for azleg.gov statute pages."""

import asyncio
import logging
import re
import ssl

import aiohttp
import certifi
from bs4 import BeautifulSoup

logger = logging.getLogger(__name__)


def _clean_text(html: str) -> str:
    """Extract and clean text from statute HTML page."""
    soup = BeautifulSoup(html, "html.parser")

    # Remove non-content elements
    for tag in soup.find_all(["script", "style", "nav", "header", "footer"]):
        tag.decompose()

    text = soup.get_text()

    # Clean up whitespace
    text = text.replace("\t", " ")
    text = re.sub(r" {2,}", " ", text)
    text = re.sub(r"\n{3,}", "\n\n", text)
    return text.strip()


async def scrape_statute(session: aiohttp.ClientSession, url: str) -> str:
    """Scrape a single statute page and return cleaned text."""
    async with session.get(url) as resp:
        if resp.status != 200:
            raise RuntimeError(f"HTTP {resp.status} for {url}")
        html = await resp.text()
        return _clean_text(html)


async def scrape_all(
    urls: dict[str, str],
    concurrency: int = 5,
    delay_ms: int = 500,
) -> dict[str, str]:
    """Scrape multiple statute pages with rate limiting.

    Args:
        urls: Mapping of section -> URL
        concurrency: Max concurrent requests
        delay_ms: Delay between requests in milliseconds

    Returns:
        Mapping of section -> cleaned text (only successful scrapes)
    """
    results: dict[str, str] = {}
    semaphore = asyncio.Semaphore(concurrency)
    total = len(urls)
    completed = 0
    failed = 0

    ssl_ctx = ssl.create_default_context(cafile=certifi.where())

    async def _scrape_one(section: str, url: str) -> None:
        nonlocal completed, failed
        async with semaphore:
            try:
                async with aiohttp.ClientSession(
                    connector=aiohttp.TCPConnector(ssl=ssl_ctx),
                ) as session:
                    text = await scrape_statute(session, url)
                    results[section] = text
            except Exception as e:
                logger.warning("Failed to scrape %s: %s", section, e)
                failed += 1
            finally:
                completed += 1
                if completed % 20 == 0 or completed == total:
                    logger.info(
                        "Scrape progress: %d/%d (%d failed)", completed, total, failed
                    )
            await asyncio.sleep(delay_ms / 1000.0)

    tasks = [_scrape_one(section, url) for section, url in urls.items()]
    await asyncio.gather(*tasks)

    logger.info(
        "Scraping complete: %d/%d succeeded, %d failed",
        len(results), total, failed,
    )
    return results
