"""
SST-Driven Sitemap Generation

Generates XML sitemaps from database content using SST configuration.

Usage:
    # Generate sitemaps for all tenants
    python manage.py generate_sitemap

    # Generate for specific tenant
    python manage.py generate_sitemap --tenant cineos

    # Include product sitemaps
    python manage.py generate_sitemap --include-products

    # Validate existing sitemaps
    python manage.py generate_sitemap --validate

    # Dry run (show what would be generated)
    python manage.py generate_sitemap --dry-run

Configuration:
    SST config: definitions/config/seo/sitemap.yaml
    Output: frontend/cdn/seo/{domain}/
"""

from __future__ import annotations

import logging
from datetime import datetime
from pathlib import Path
from typing import TYPE_CHECKING, Any
from xml.etree import ElementTree as ET

import yaml
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError

if TYPE_CHECKING:
    from argparse import ArgumentParser

logger = logging.getLogger(__name__)

# XML namespaces
SITEMAP_NS = "http://www.sitemaps.org/schemas/sitemap/0.9"
SITEMAP_INDEX_NS = "http://www.sitemaps.org/schemas/sitemap/0.9"
IMAGE_NS = "http://www.google.com/schemas/sitemap-image/1.1"


def load_sitemap_config() -> dict[str, Any]:
    """Load sitemap configuration from SST YAML."""
    # Find the SST config file
    config_paths = [
        Path(__file__).parent.parent.parent / "definitions" / "config" / "seo" / "sitemap.yaml",
        Path(settings.BASE_DIR).parent.parent
        / "packages"
        / "lightwave-core"
        / "lightwave"
        / "schema"
        / "definitions"
        / "config"
        / "seo"
        / "sitemap.yaml",
    ]

    for config_path in config_paths:
        if config_path.exists():
            with open(config_path) as f:
                return yaml.safe_load(f)

    # Return defaults if no config found
    logger.warning("SST sitemap config not found, using defaults")
    return {
        "page_types": {"default": {"changefreq": "monthly", "priority": 0.5, "include": True}},
        "robots": {
            "user_agents": [
                {
                    "agent": "*",
                    "allow": ["/"],
                    "disallow": ["/admin/", "/api/", "/accounts/"],
                }
            ]
        },
        "output": {"directory": "cdn/seo/{domain}/"},
        "products": {"enabled": False},
        "index": {"enabled": True},
    }


class Command(BaseCommand):
    help = "Generate XML sitemaps from SST configuration"

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        super().__init__(*args, **kwargs)
        self.config = load_sitemap_config()

    def add_arguments(self, parser: "ArgumentParser") -> None:
        parser.add_argument(
            "--tenant",
            type=str,
            help="Generate sitemap for specific tenant only",
        )
        parser.add_argument(
            "--output-dir",
            type=str,
            help="Output directory for sitemaps (default: from SST config)",
        )
        parser.add_argument(
            "--dry-run",
            action="store_true",
            help="Show what would be generated without writing files",
        )
        parser.add_argument(
            "--validate",
            action="store_true",
            help="Validate existing sitemaps against Google schema",
        )
        parser.add_argument(
            "--include-drafts",
            action="store_true",
            help="Include draft pages in sitemap (for testing)",
        )
        parser.add_argument(
            "--include-products",
            action="store_true",
            help="Include product sitemaps for e-commerce sites",
        )
        parser.add_argument(
            "--json",
            action="store_true",
            help="Output results as JSON",
        )

    def handle(self, *args: Any, **options: Any) -> None:
        tenant_slug = options.get("tenant")
        output_dir = options.get("output_dir")
        dry_run = options.get("dry_run", False)
        validate_only = options.get("validate", False)
        include_drafts = options.get("include_drafts", False)
        include_products = options.get("include_products", False)
        json_output = options.get("json", False)

        # Determine output directory from SST config
        if output_dir:
            base_output = Path(output_dir)
        else:
            output_template = self.config.get("output", {}).get("directory", "cdn/seo/{domain}/")
            # Base path is frontend/ directory
            base_output = Path(settings.BASE_DIR).parent / "frontend" / output_template.split("{domain}")[0].strip("/")

        if validate_only:
            return self._validate_sitemaps(base_output, tenant_slug)

        # Import models (use correct path for v5.0+ architecture)
        try:
            from apps.platform.site.models import Site
            from apps.platform.site.page.models import Page
        except ImportError:
            try:
                # Fallback for older structure
                from apps.cms.models import Page, Site
            except ImportError as e:
                raise CommandError("Site/Page models not available. Ensure apps.platform.site is installed.") from e

        # Get sites to process
        sites = Site.objects.filter(is_active=True)
        if tenant_slug:
            sites = sites.filter(domain__icontains=tenant_slug)

        if not sites.exists():
            self.stdout.write(self.style.WARNING("No active sites found."))
            return

        results = {
            "sites_processed": 0,
            "total_pages": 0,
            "total_products": 0,
            "files_generated": [],
        }

        for site in sites:
            site_result = self._process_site(
                site=site,
                Page=Page,
                base_output=base_output,
                include_drafts=include_drafts,
                include_products=include_products,
                dry_run=dry_run,
            )
            results["sites_processed"] += 1
            results["total_pages"] += site_result["pages"]
            results["total_products"] += site_result["products"]
            results["files_generated"].extend(site_result["files"])

        # Summary
        self._print_summary(results, dry_run, json_output)

    def _process_site(
        self,
        site: Any,
        Page: Any,
        base_output: Path,
        include_drafts: bool,
        include_products: bool,
        dry_run: bool,
    ) -> dict[str, Any]:
        """Process a single site and generate its sitemaps."""
        self.stdout.write(f"\nProcessing: {site.domain}")

        result = {"pages": 0, "products": 0, "files": []}

        # Get tenant-specific config overrides
        tenant_config = self._get_tenant_config(site.domain)

        # Output directory for this site
        output_template = self.config.get("output", {}).get("directory", "cdn/seo/{domain}/")
        site_output_dir = base_output / site.domain
        if "{domain}" in output_template:
            # Parse the template
            parts = output_template.split("{domain}")
            site_output_dir = base_output.parent / parts[0].strip("/") / site.domain
            if len(parts) > 1 and parts[1]:
                site_output_dir = site_output_dir / parts[1].strip("/")

        # 1. Generate pages sitemap
        pages = Page.objects.filter(site=site)
        if not include_drafts:
            pages = pages.filter(status="published")

        # Filter by page types that should be included
        page_types_config = self.config.get("page_types", {})
        excluded_types = [pt for pt, cfg in page_types_config.items() if not cfg.get("include", True)]
        if excluded_types:
            pages = pages.exclude(page_type__in=excluded_types)

        page_count = pages.count()
        result["pages"] = page_count

        if page_count > 0:
            sitemap_xml = self._build_sitemap(site, pages)
            sitemap_path = site_output_dir / "sitemap.xml"

            if dry_run:
                self.stdout.write(f"  Would create: {sitemap_path} ({page_count} URLs)")
            else:
                site_output_dir.mkdir(parents=True, exist_ok=True)
                sitemap_path.write_text(sitemap_xml)
                result["files"].append(str(sitemap_path))
                self.stdout.write(self.style.SUCCESS(f"  ✓ {sitemap_path} ({page_count} URLs)"))
        else:
            self.stdout.write(self.style.WARNING(f"  No published pages for {site.domain}"))

        # 2. Generate product sitemap (if enabled)
        products_config = tenant_config.get("products", self.config.get("products", {}))
        if include_products and products_config.get("enabled", False):
            product_result = self._generate_product_sitemap(site, site_output_dir, products_config, dry_run)
            result["products"] = product_result.get("count", 0)
            result["files"].extend(product_result.get("files", []))

        # 3. Generate sitemap index
        if self.config.get("index", {}).get("enabled", True) and result["pages"] > 0:
            index_xml = self._build_sitemap_index(site, result)
            index_path = site_output_dir / "sitemap-index.xml"

            if dry_run:
                self.stdout.write(f"  Would create: {index_path}")
            else:
                index_path.write_text(index_xml)
                result["files"].append(str(index_path))
                self.stdout.write(self.style.SUCCESS(f"  ✓ {index_path}"))

        # 4. Generate robots.txt
        robots_txt = self._build_robots(site)
        robots_path = site_output_dir / "robots.txt"

        if dry_run:
            self.stdout.write(f"  Would create: {robots_path}")
        else:
            robots_path.write_text(robots_txt)
            result["files"].append(str(robots_path))
            self.stdout.write(self.style.SUCCESS(f"  ✓ {robots_path}"))

        return result

    def _get_tenant_config(self, domain: str) -> dict[str, Any]:
        """Get tenant-specific configuration overrides."""
        overrides = self.config.get("tenant_overrides", {})

        # Match by domain substring
        for tenant_key, tenant_config in overrides.items():
            if tenant_key in domain:
                # Merge with base config
                merged = {**self.config}
                for key, value in tenant_config.items():
                    if isinstance(value, dict) and key in merged:
                        merged[key] = {**merged[key], **value}
                    else:
                        merged[key] = value
                return merged

        return self.config

    def _build_sitemap(self, site: Any, pages: Any) -> str:
        """Build sitemap XML for a site."""
        ET.register_namespace("", SITEMAP_NS)

        urlset = ET.Element("urlset", xmlns=SITEMAP_NS)

        # Add generation comment
        comment = ET.Comment(
            f" Generated by: python manage.py generate_sitemap\n"
            f"     Source: SST definitions/config/seo/sitemap.yaml\n"
            f"     Generated at: {datetime.now().isoformat()}\n"
            f"     Site: {site.domain}\n"
            f"     Total URLs: {pages.count()} "
        )
        urlset.insert(0, comment)

        page_types_config = self.config.get("page_types", {})
        default_config = page_types_config.get("default", {})

        for page in pages:
            url_elem = ET.SubElement(urlset, "url")

            # Location (required)
            loc = ET.SubElement(url_elem, "loc")
            loc.text = f"https://{site.domain}{page.path}"

            # Last modified
            lastmod_date = getattr(page, "updated_at", None) or getattr(page, "published_at", None)
            if lastmod_date:
                lastmod = ET.SubElement(url_elem, "lastmod")
                lastmod.text = lastmod_date.strftime("%Y-%m-%d")

            # Get page type config
            page_type = getattr(page, "page_type", "default")
            type_config = page_types_config.get(page_type, default_config)

            # Change frequency (from SST)
            changefreq = ET.SubElement(url_elem, "changefreq")
            changefreq.text = type_config.get("changefreq", "monthly")

            # Priority (from SST, adjusted by depth)
            base_priority = type_config.get("priority", 0.5)
            if page.path == "/":
                final_priority = 1.0
            else:
                depth = page.path.count("/") - 1
                final_priority = max(0.1, base_priority - (depth * 0.1))

            priority = ET.SubElement(url_elem, "priority")
            priority.text = str(round(final_priority, 1))

        ET.indent(urlset, space="  ")
        return ET.tostring(urlset, encoding="unicode", xml_declaration=True)

    def _build_sitemap_index(self, site: Any, result: dict[str, Any]) -> str:
        """Build sitemap index XML."""
        ET.register_namespace("", SITEMAP_INDEX_NS)

        sitemapindex = ET.Element("sitemapindex", xmlns=SITEMAP_INDEX_NS)

        # Add comment
        comment = ET.Comment(f" Sitemap Index for {site.domain}\n" f"     Generated at: {datetime.now().isoformat()} ")
        sitemapindex.insert(0, comment)

        # Add pages sitemap
        if result["pages"] > 0:
            sitemap = ET.SubElement(sitemapindex, "sitemap")
            loc = ET.SubElement(sitemap, "loc")
            loc.text = f"https://{site.domain}/sitemap.xml"
            lastmod = ET.SubElement(sitemap, "lastmod")
            lastmod.text = datetime.now().strftime("%Y-%m-%d")

        # Add products sitemap
        if result["products"] > 0:
            sitemap = ET.SubElement(sitemapindex, "sitemap")
            loc = ET.SubElement(sitemap, "loc")
            loc.text = f"https://{site.domain}/sitemap-products.xml"
            lastmod = ET.SubElement(sitemap, "lastmod")
            lastmod.text = datetime.now().strftime("%Y-%m-%d")

        ET.indent(sitemapindex, space="  ")
        return ET.tostring(sitemapindex, encoding="unicode", xml_declaration=True)

    def _build_robots(self, site: Any) -> str:
        """Build robots.txt from SST configuration."""
        robots_config = self.config.get("robots", {})
        lines = [
            f"# Robots.txt for {site.domain}",
            "# Generated from SST: definitions/config/seo/sitemap.yaml",
            f"# Generated at: {datetime.now().isoformat()}",
            "",
        ]

        # User agent rules
        for ua_rule in robots_config.get("user_agents", []):
            agent = ua_rule.get("agent", "*")
            lines.append(f"User-agent: {agent}")

            for allow in ua_rule.get("allow", []):
                lines.append(f"Allow: {allow}")

            for disallow in ua_rule.get("disallow", []):
                lines.append(f"Disallow: {disallow}")

            lines.append("")

        # Crawl delay
        crawl_delay = robots_config.get("crawl_delay")
        if crawl_delay:
            lines.append(f"Crawl-delay: {crawl_delay}")
            lines.append("")

        # Sitemap references
        lines.append("# Sitemaps")
        lines.append(f"Sitemap: https://{site.domain}/sitemap-index.xml")

        return "\n".join(lines)

    def _generate_product_sitemap(
        self,
        site: Any,
        output_dir: Path,
        products_config: dict[str, Any],
        dry_run: bool,
    ) -> dict[str, Any]:
        """Generate product sitemap for e-commerce sites."""
        result = {"count": 0, "files": []}

        try:
            # Try to import product model (e-commerce sites)
            from apps.platform.shop.models import Product
        except ImportError:
            self.stdout.write(self.style.WARNING("  Product model not available, skipping product sitemap"))
            return result

        # Get products
        include_statuses = products_config.get("include_statuses", ["published"])
        products = Product.objects.filter(
            site=site,
            status__in=include_statuses,
        )

        product_count = products.count()
        if product_count == 0:
            self.stdout.write(self.style.WARNING(f"  No products for {site.domain}"))
            return result

        result["count"] = product_count

        # Build product sitemap
        ET.register_namespace("", SITEMAP_NS)
        if products_config.get("include_images", False):
            ET.register_namespace("image", IMAGE_NS)

        urlset = ET.Element("urlset", xmlns=SITEMAP_NS)

        for product in products:
            url_elem = ET.SubElement(urlset, "url")

            loc = ET.SubElement(url_elem, "loc")
            loc.text = f"https://{site.domain}/shop/products/{product.slug}/"

            if hasattr(product, "updated_at") and product.updated_at:
                lastmod = ET.SubElement(url_elem, "lastmod")
                lastmod.text = product.updated_at.strftime("%Y-%m-%d")

            changefreq = ET.SubElement(url_elem, "changefreq")
            changefreq.text = products_config.get("changefreq", "weekly")

            priority = ET.SubElement(url_elem, "priority")
            priority.text = str(products_config.get("priority", 0.7))

            # Add image if enabled and available
            if products_config.get("include_images", False) and hasattr(product, "image_url"):
                image_elem = ET.SubElement(url_elem, f"{{{IMAGE_NS}}}image")
                image_loc = ET.SubElement(image_elem, f"{{{IMAGE_NS}}}loc")
                image_loc.text = product.image_url

        ET.indent(urlset, space="  ")
        sitemap_xml = ET.tostring(urlset, encoding="unicode", xml_declaration=True)

        sitemap_path = output_dir / "sitemap-products.xml"

        if dry_run:
            self.stdout.write(f"  Would create: {sitemap_path} ({product_count} products)")
        else:
            sitemap_path.write_text(sitemap_xml)
            result["files"].append(str(sitemap_path))
            self.stdout.write(self.style.SUCCESS(f"  ✓ {sitemap_path} ({product_count} products)"))

        return result

    def _validate_sitemaps(self, base_output: Path, tenant_slug: str | None = None) -> None:
        """Validate existing sitemaps against Google requirements."""
        self.stdout.write("Validating sitemaps (using SST validation rules)...\n")

        validation_config = self.config.get("validation", {})
        max_size = validation_config.get("max_file_size_mb", 50) * 1024 * 1024
        max_urls = validation_config.get("max_urls_per_file", 50000)

        # Find sitemap files
        pattern = f"*{tenant_slug}*" if tenant_slug else "*"
        sitemap_files = list(base_output.glob(f"{pattern}/sitemap*.xml"))

        if not sitemap_files:
            self.stdout.write(self.style.WARNING("No sitemaps found to validate."))
            return

        errors = []

        for sitemap_path in sitemap_files:
            self.stdout.write(f"Checking: {sitemap_path}")

            try:
                tree = ET.parse(sitemap_path)
                root = tree.getroot()

                # Count URLs
                urls = root.findall(".//{%s}url" % SITEMAP_NS) or root.findall(".//url")
                url_count = len(urls)

                # Check limits
                if url_count > max_urls:
                    errors.append(f"{sitemap_path}: Too many URLs ({url_count} > {max_urls})")

                file_size = sitemap_path.stat().st_size
                if file_size > max_size:
                    errors.append(f"{sitemap_path}: File too large ({file_size / 1024 / 1024:.1f}MB)")

                # Check required elements
                for url_elem in urls:
                    loc = url_elem.find("{%s}loc" % SITEMAP_NS) or url_elem.find("loc")
                    if loc is None or not loc.text:
                        errors.append(f"{sitemap_path}: URL missing <loc> element")
                        break

                self.stdout.write(self.style.SUCCESS(f"  ✓ Valid ({url_count} URLs, {file_size / 1024:.1f}KB)"))

            except ET.ParseError as e:
                errors.append(f"{sitemap_path}: XML parse error: {e}")
            except Exception as e:
                errors.append(f"{sitemap_path}: Error: {e}")

        # Summary
        self.stdout.write("\n" + "=" * 50)
        if errors:
            self.stdout.write(self.style.ERROR(f"Validation failed with {len(errors)} error(s):"))
            for error in errors:
                self.stdout.write(self.style.ERROR(f"  ✗ {error}"))
        else:
            self.stdout.write(self.style.SUCCESS(f"✓ All {len(sitemap_files)} sitemaps valid!"))

    def _print_summary(self, results: dict[str, Any], dry_run: bool, json_output: bool) -> None:
        """Print generation summary."""
        import json

        if json_output:
            self.stdout.write(json.dumps(results, indent=2))
            return

        self.stdout.write("\n" + "=" * 50)
        if dry_run:
            self.stdout.write("Dry run complete.")
            self.stdout.write(f"Would generate sitemaps for {results['total_pages']} pages")
            if results["total_products"] > 0:
                self.stdout.write(f"Would include {results['total_products']} products")
        else:
            self.stdout.write(
                self.style.SUCCESS(
                    f"Generated {len(results['files_generated'])} files for " f"{results['total_pages']} pages"
                )
            )
            if results["total_products"] > 0:
                self.stdout.write(f"Included {results['total_products']} products")
