import json
import logging
from datetime import datetime
from xml.etree.ElementTree import Element, tostring
from xml.dom import minidom
from devplacepy.config import SITE_URL
from devplacepy.utils import strip_html

logger = logging.getLogger(__name__)


SITE_NAME = "DevPlace"


def truncate(text, max_len=160):
    if not text:
        return ""
    text = " ".join(text.split())
    if len(text) <= max_len:
        return text
    text = text[:max_len - 3].rsplit(" ", 1)[0]
    return text + "..."


def site_url(request):
    return SITE_URL or str(request.base_url).rstrip("/")


def website_schema(base_url):
    return {
        "@type": "WebSite",
        "name": SITE_NAME,
        "url": base_url,
        "potentialAction": {
            "@type": "SearchAction",
            "target": {
                "@type": "EntryPoint",
                "urlTemplate": f"{base_url}/feed?search={{query}}"
            },
            "query-input": "required name=query"
        }
    }


def breadcrumb_schema(items, base_url):
    return {
        "@type": "BreadcrumbList",
        "itemListElement": [
            {
                "@type": "ListItem",
                "position": i + 1,
                "item": {
                    "@id": item["url"] if item["url"].startswith("http") else f"{base_url}{item['url']}",
                    "name": item["name"]
                }
            }
            for i, item in enumerate(items)
        ]
    }


def discussion_forum_posting(post, author, comment_count, star_count, base_url):
    schema = {
        "@type": "DiscussionForumPosting",
        "headline": post.get("title") or "Untitled",
        "text": truncate(post.get("content", ""), 500),
        "url": f"{base_url}/posts/{post.get('slug') or post['uid']}",
        "author": {
            "@type": "Person",
            "name": author["username"] if author else "Unknown",
            "url": f"{base_url}/profile/{author['username']}" if author else ""
        },
        "datePublished": post.get("created_at", ""),
        "interactionStatistic": [
            {"@type": "InteractionCounter", "interactionType": "https://schema.org/LikeAction", "userInteractionCount": star_count},
            {"@type": "InteractionCounter", "interactionType": "https://schema.org/CommentAction", "userInteractionCount": comment_count}
        ]
    }
    return schema


def profile_page_schema(profile_user, post_count, base_url):
    return {
        "@type": "ProfilePage",
        "mainEntity": {
            "@type": "Person",
            "name": profile_user.get("username", ""),
            "alternateName": profile_user.get("username", ""),
            "description": profile_user.get("bio", "") or f"Developer on {SITE_NAME}",
            "interactionStatistic": [
                {"@type": "InteractionCounter", "interactionType": "https://schema.org/WriteAction", "userInteractionCount": post_count}
            ]
        }
    }


def software_application_schema(project, base_url):
    return {
        "@type": "SoftwareApplication",
        "name": project.get("title", "Untitled"),
        "description": truncate(project.get("description", ""), 300),
        "url": f"{base_url}/projects/{project.get('slug') or project['uid']}",
        "applicationCategory": "DeveloperApplication",
        "operatingSystem": project.get("platforms", "Cross-platform"),
        "author": {
            "@type": "Person",
            "name": project.get("author_name", "Unknown")
        },
        "datePublished": project.get("created_at", ""),
        "offers": {
            "@type": "Offer",
            "price": "0",
            "priceCurrency": "USD"
        }
    }


def organization_schema(base_url):
    return {
        "@type": "Organization",
        "name": SITE_NAME,
        "url": base_url,
        "logo": f"{base_url}{DEFAULT_OG_IMAGE}",
    }


def news_article_schema(article, base_url, image_url=""):
    url = f"{base_url}/news/{article.get('slug') or article['uid']}"
    schema = {
        "@type": "NewsArticle",
        "headline": (article.get("title") or "Untitled")[:110],
        "description": truncate(strip_html(article.get("description", "") or ""), 200),
        "url": url,
        "datePublished": article.get("synced_at", "") or article.get("created_at", ""),
        "mainEntityOfPage": {"@type": "WebPage", "@id": url},
        "author": {"@type": "Organization", "name": article.get("source_name") or SITE_NAME},
        "publisher": organization_schema(base_url),
    }
    if image_url:
        schema["image"] = image_url
    return schema


def software_source_code_schema(gist, base_url):
    return {
        "@type": "SoftwareSourceCode",
        "name": gist.get("title") or "Gist",
        "description": truncate(strip_html(gist.get("description", "") or ""), 200),
        "url": f"{base_url}/gists/{gist.get('slug') or gist['uid']}",
        "programmingLanguage": gist.get("language", "") or "text",
        "dateCreated": gist.get("created_at", ""),
    }


def combine(schemas):
    if not schemas:
        return None
    cleaned = []
    for s in schemas:
        if s is not None:
            s.pop("@context", None)
            cleaned.append(s)
    if not cleaned:
        return None
    if len(cleaned) == 1:
        return json.dumps({"@context": "https://schema.org", **cleaned[0]}, ensure_ascii=False)
    return json.dumps({"@context": "https://schema.org", "@graph": cleaned}, ensure_ascii=False)


DEFAULT_OG_IMAGE = "/static/og-default.png"


def base_seo_context(request, title="", description="", robots="index,follow", og_type="website", og_image=None, breadcrumbs=None, schemas=None):
    base = site_url(request)
    page_title = f"{title} - {SITE_NAME}" if title else SITE_NAME
    canonical = f"{base}{request.url.path}"
    page = request.query_params.get("page")
    if page and page not in ("", "1"):
        canonical = f"{canonical}?page={page}"
    clean_description = truncate(strip_html(description), 160)
    og_img = og_image or f"{base}{DEFAULT_OG_IMAGE}"
    return {
        "page_title": page_title,
        "meta_description": clean_description,
        "meta_robots": robots,
        "canonical_url": canonical,
        "og_title": title or SITE_NAME,
        "og_description": clean_description,
        "og_image": og_img,
        "og_type": og_type,
        "breadcrumbs": breadcrumbs or [],
        "page_schema": combine(schemas or []),
    }


def make_sitemap(base_url):
    from devplacepy.database import get_table, db

    def url_element(loc, lastmod=None, changefreq=None, priority=None):
        u = Element("url")
        loc_el = Element("loc")
        loc_el.text = loc
        u.append(loc_el)
        if lastmod:
            lm = Element("lastmod")
            lm.text = lastmod
            u.append(lm)
        if changefreq:
            cf = Element("changefreq")
            cf.text = changefreq
            u.append(cf)
        if priority is not None:
            pr = Element("priority")
            pr.text = str(priority)
            u.append(pr)
        return u

    urlset = Element("urlset")
    urlset.set("xmlns", "http://www.sitemaps.org/schemas/sitemap/0.9")

    urlset.append(url_element(f"{base_url}/", changefreq="daily", priority="1.0"))
    urlset.append(url_element(f"{base_url}/feed", changefreq="hourly", priority="0.9"))
    urlset.append(url_element(f"{base_url}/news", changefreq="hourly", priority="0.9"))
    urlset.append(url_element(f"{base_url}/projects", changefreq="daily", priority="0.8"))
    urlset.append(url_element(f"{base_url}/gists", changefreq="daily", priority="0.8"))

    if "posts" in db.tables:
        posts = list(get_table("posts").find(order_by=["-created_at"], _limit=500))
        for p in posts:
            urlset.append(url_element(
                f"{base_url}/posts/{p.get('slug') or p['uid']}",
                lastmod=p.get("created_at", ""),
                changefreq="weekly",
                priority="0.7"
            ))

    if "projects" in db.tables:
        projects = list(get_table("projects").find(order_by=["-created_at"], _limit=1000))
        for p in projects:
            urlset.append(url_element(
                f"{base_url}/projects/{p.get('slug') or p['uid']}",
                lastmod=p.get("created_at", ""),
                changefreq="weekly",
                priority="0.6"
            ))

    if "gists" in db.tables:
        gists = list(get_table("gists").find(order_by=["-created_at"], _limit=500))
        for g in gists:
            urlset.append(url_element(
                f"{base_url}/gists/{g.get('slug') or g['uid']}",
                lastmod=g.get("created_at", ""),
                changefreq="weekly",
                priority="0.6"
            ))

    if "news" in db.tables:
        articles = list(get_table("news").find(status="published", order_by=["-synced_at"], _limit=500))
        for a in articles:
            urlset.append(url_element(
                f"{base_url}/news/{a.get('slug') or a['uid']}",
                lastmod=a.get("synced_at", "") or a.get("created_at", ""),
                changefreq="weekly",
                priority="0.7"
            ))

    if "users" in db.tables:
        post_counts = {}
        if "posts" in db.tables:
            for row in db.query("SELECT user_uid, COUNT(*) AS c FROM posts GROUP BY user_uid"):
                post_counts[row["user_uid"]] = row["c"]
        users = list(get_table("users").find(order_by=["-created_at"], _limit=200))
        for u in users:
            if post_counts.get(u["uid"], 0) < 2:
                continue
            urlset.append(url_element(
                f"{base_url}/profile/{u['username']}",
                lastmod=u.get("created_at", ""),
                changefreq="weekly",
                priority="0.4"
            ))

    rough = tostring(urlset, encoding="unicode")
    dom = minidom.parseString(rough)
    return dom.toprettyxml(indent="  ")
