"""Track A: research existing live blogs from blog_list_url."""

from __future__ import annotations

import json
import logging
from typing import Any
from urllib.parse import urljoin, urlparse

import requests
from django.conf import settings as django_settings
from django.utils import timezone

from blogs.models import BlogSettings
from blogs.services.anthropic_utils import claude_json, claude_json_object

logger = logging.getLogger(__name__)

HTML_MAX_CHARS = 80_000


def _fetch_html(url: str) -> str:
    timeout = getattr(django_settings, "BLOG_FETCH_TIMEOUT", 30)
    resp = requests.get(
        url,
        timeout=timeout,
        headers={"User-Agent": "ShopifyBlogBot/1.0"},
    )
    resp.raise_for_status()
    return (resp.text or "")[:HTML_MAX_CHARS]


def _discover_posts(list_html: str, base_url: str) -> list[dict[str, str]]:
    system = (
        "You extract blog post links from an HTML blog listing page. "
        "Return ONLY a valid JSON array of objects. Each object has keys: "
        "title, url, snippet (optional string). "
        "Use absolute URLs. Only blog article pages, not tags/categories/pagination. "
        "Keep titles short; escape any double quotes inside strings."
    )
    user = (
        f"Base URL: {base_url}\n\n"
        f"HTML:\n{list_html[:HTML_MAX_CHARS]}\n\n"
        f"Return at most {getattr(django_settings, 'BLOG_RESEARCH_MAX_DISCOVER', 30)} posts."
    )
    try:
        return claude_json(system, user, temperature=0.2)
    except (json.JSONDecodeError, ValueError) as exc:
        logger.warning("discover_posts JSON failed: %s", exc)
        raise


def _summarize_post(post_html: str, url: str) -> dict[str, Any]:
    system = (
        "You analyze a single blog post HTML page. "
        "Return ONLY one JSON object with: title, summary (2-3 sentences), "
        "main_topics (array of 3-8 keyword strings), angle (short phrase)."
    )
    user = f"URL: {url}\n\nHTML:\n{post_html[:HTML_MAX_CHARS]}"
    item = claude_json_object(system, user, max_tokens=1500, temperature=0.3)
    item["url"] = url
    return item


def _shopify_fallback_inventory(settings_obj: BlogSettings) -> list[dict[str, Any]]:
    from blogs.services.shopify_blog_service import ShopifyBlogService

    blog_id = (settings_obj.shopify_blog_id or "").strip()
    if not blog_id:
        return []
    articles = ShopifyBlogService.list_articles(blog_id, limit=50)
    base = ShopifyBlogService._base_url()
    out = []
    for a in articles:
        handle = a.get("handle") or ""
        title = a.get("title") or ""
        art_id = a.get("id")
        url = f"{base}/blogs/{blog_id}/articles/{handle}" if handle else ""
        if not url and art_id:
            url = f"{base}/admin/articles/{art_id}"
        out.append(
            {
                "title": title,
                "url": url,
                "summary": (a.get("summary_html") or "")[:500],
                "main_topics": [],
                "angle": "shopify_api",
                "source": "shopify_api",
            }
        )
    return out


def refresh_external_blog_inventory(settings_obj: BlogSettings | None = None) -> list[dict]:
    settings_obj = settings_obj or BlogSettings.load()
    blog_list_url = (settings_obj.blog_list_url or "").strip()
    if not blog_list_url:
        settings_obj.external_blog_inventory_error = "blog_list_url is not set"
        settings_obj.save(update_fields=["external_blog_inventory_error", "updated_at"])
        return settings_obj.external_blog_inventory or []

    inventory: list[dict[str, Any]] = []
    errors: list[str] = []

    try:
        list_html = _fetch_html(blog_list_url)
        discovered = _discover_posts(list_html, blog_list_url)
        max_posts = getattr(django_settings, "BLOG_RESEARCH_MAX_POSTS", 15)

        for entry in discovered[:max_posts]:
            raw_url = (entry.get("url") or "").strip()
            if not raw_url:
                continue
            if raw_url.startswith("/"):
                parsed = urlparse(blog_list_url)
                raw_url = urljoin(f"{parsed.scheme}://{parsed.netloc}", raw_url)
            try:
                post_html = _fetch_html(raw_url)
                summary = _summarize_post(post_html, raw_url)
                if not summary.get("title") and entry.get("title"):
                    summary["title"] = entry["title"]
                if entry.get("snippet") and not summary.get("summary"):
                    summary["summary"] = entry["snippet"]
                inventory.append(summary)
            except Exception as exc:
                logger.warning("post fetch failed url=%s: %s", raw_url, exc)
                errors.append(f"{raw_url}: {exc}")
                if entry.get("title"):
                    inventory.append(
                        {
                            "title": entry["title"],
                            "url": raw_url,
                            "summary": entry.get("snippet") or "",
                            "main_topics": [],
                            "angle": "listing_only",
                        }
                    )

        if not inventory:
            inventory = _shopify_fallback_inventory(settings_obj)
            if inventory:
                errors.append("HTML scrape found no posts; used Shopify API fallback.")

    except Exception as exc:
        logger.exception("blog list research failed")
        errors.append(str(exc))
        try:
            inventory = _shopify_fallback_inventory(settings_obj)
        except Exception as fb_exc:
            errors.append(f"Shopify fallback failed: {fb_exc}")

    settings_obj.external_blog_inventory = inventory
    settings_obj.external_blog_inventory_updated_at = timezone.now()
    settings_obj.external_blog_inventory_error = "; ".join(errors[:5])
    settings_obj.save(
        update_fields=[
            "external_blog_inventory",
            "external_blog_inventory_updated_at",
            "external_blog_inventory_error",
            "updated_at",
        ]
    )
    return inventory
