"""
Management command to validate schema package externalization readiness.

This command provides a comprehensive checklist for publishing the
lightwave-core schema package externally. It runs all consistency checks
plus additional requirements for public distribution.

Externalization Requirements:
1. All consistency checks pass (no errors)
2. All YAML files have complete _meta sections
3. All public schemas are documented
4. No internal-only references leak
5. Version consistency across files
6. LICENSE and CHANGELOG present
7. pyproject.toml metadata complete

Usage:
    # Check externalization readiness
    python manage.py validate_externalization

    # Verbose output
    python manage.py validate_externalization -v 2

    # Generate externalization report
    python manage.py validate_externalization --format json > report.json
"""

from __future__ import annotations

import json
import re
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any

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

from lightwave.schema.management.commands.validate_consistency import (
    ConsistencyReport,
    CoverageValidator,
    DeploymentCrossRefValidator,
    SelfFormatValidator,
    SuiteReport,
    UserFlowsValidator,
    ValidationResult,
    ValidationSeverity,
)
from lightwave.schema.validation.cross_validator import CrossReferenceValidator


@dataclass
class ExternalizationCheck:
    """A single externalization readiness check."""

    name: str
    category: str
    passed: bool
    message: str
    severity: ValidationSeverity = ValidationSeverity.ERROR
    suggestion: str = ""


@dataclass
class ExternalizationReport:
    """Full externalization readiness report."""

    consistency_report: ConsistencyReport
    checks: list[ExternalizationCheck] = field(default_factory=list)
    package_info: dict[str, Any] = field(default_factory=dict)

    @property
    def is_ready(self) -> bool:
        """Check if package is ready for externalization."""
        # Must pass all consistency checks
        if not self.consistency_report.is_valid:
            return False
        # Must pass all required externalization checks
        required_checks = [c for c in self.checks if c.severity == ValidationSeverity.ERROR]
        return all(c.passed for c in required_checks)

    @property
    def blocking_issues(self) -> list[ExternalizationCheck]:
        """Get list of blocking issues."""
        return [c for c in self.checks if not c.passed and c.severity == ValidationSeverity.ERROR]

    @property
    def warnings(self) -> list[ExternalizationCheck]:
        """Get list of warnings."""
        return [c for c in self.checks if not c.passed and c.severity == ValidationSeverity.WARNING]

    def to_dict(self) -> dict[str, Any]:
        return {
            "ready": self.is_ready,
            "package_info": self.package_info,
            "consistency": self.consistency_report.to_dict(),
            "checks": [
                {
                    "name": c.name,
                    "category": c.category,
                    "passed": c.passed,
                    "message": c.message,
                    "severity": c.severity.value,
                    "suggestion": c.suggestion,
                }
                for c in self.checks
            ],
            "blocking_count": len(self.blocking_issues),
            "warning_count": len(self.warnings),
        }


class ExternalizationValidator:
    """
    Validates schema package is ready for external distribution.

    Beyond consistency checks, verifies:
    - Package metadata is complete
    - Documentation is present
    - No internal-only references
    - Version consistency
    """

    def __init__(self):
        # Schema package root
        self.package_root = Path(__file__).parents[3]  # lightwave/schema -> lightwave -> lightwave-core
        self.schema_dir = self.package_root / "lightwave" / "schema"
        self.definitions_dir = self.schema_dir / "definitions"

    def validate_all(self) -> ExternalizationReport:
        """Run full externalization validation."""
        # First run consistency checks
        consistency = self._run_consistency_checks()

        report = ExternalizationReport(consistency_report=consistency)

        # Gather package info
        report.package_info = self._gather_package_info()

        # Run externalization-specific checks
        self._check_package_metadata(report)
        self._check_documentation(report)
        self._check_version_consistency(report)
        self._check_no_internal_refs(report)
        self._check_exports(report)

        return report

    def _run_consistency_checks(self) -> ConsistencyReport:
        """Run all consistency validation suites."""
        consistency = ConsistencyReport()

        # Self-format validation
        validator = SelfFormatValidator(self.definitions_dir)
        consistency.suites["self-format"] = validator.validate_all()

        # Cross-reference validation
        cross_validator = CrossReferenceValidator(self.definitions_dir)
        cross_report = cross_validator.validate_all()
        suite_report = SuiteReport(name="cross-refs")
        for issue in cross_report.issues:
            suite_report.results.append(
                ValidationResult(
                    file=issue.file,
                    rule=issue.issue_type,
                    severity=ValidationSeverity.ERROR,
                    message=issue.message,
                    location=issue.location,
                    value=issue.value,
                    suggestion=issue.suggestion or "",
                )
            )
        for warning in cross_report.warnings:
            suite_report.results.append(
                ValidationResult(
                    file=warning.file,
                    rule=warning.issue_type,
                    severity=ValidationSeverity.WARNING,
                    message=warning.message,
                    location=warning.location,
                    value=warning.value,
                    suggestion=warning.suggestion or "",
                )
            )
        suite_report.stats = cross_report.stats
        consistency.suites["cross-refs"] = suite_report

        # Coverage validation
        coverage_validator = CoverageValidator(self.definitions_dir)
        consistency.suites["coverage"] = coverage_validator.validate_all()

        # User flows validation
        flows_validator = UserFlowsValidator(self.definitions_dir)
        consistency.suites["user-flows"] = flows_validator.validate_all()

        # Deployment cross-refs
        deploy_validator = DeploymentCrossRefValidator(self.definitions_dir)
        consistency.suites["deployment"] = deploy_validator.validate_all()

        return consistency

    def _gather_package_info(self) -> dict[str, Any]:
        """Gather package metadata."""
        info = {
            "name": "lightwave-core",
            "version": "unknown",
            "schema_count": 0,
            "definition_count": 0,
        }

        # Try to get version from pyproject.toml
        pyproject_path = self.package_root / "pyproject.toml"
        if pyproject_path.exists():
            try:
                import tomllib

                with open(pyproject_path, "rb") as f:
                    pyproject = tomllib.load(f)
                info["version"] = pyproject.get("project", {}).get("version", "unknown")
                info["name"] = pyproject.get("project", {}).get("name", "lightwave-core")
            except Exception:
                pass

        # Count schemas
        if self.definitions_dir.exists():
            yaml_files = list(self.definitions_dir.glob("*.yaml"))
            info["schema_count"] = len(yaml_files)

            # Count definitions in all files
            total_defs = 0
            for yaml_path in yaml_files:
                try:
                    with open(yaml_path) as f:
                        data = yaml.safe_load(f) or {}
                    # Count top-level non-meta keys
                    total_defs += sum(1 for k in data.keys() if not k.startswith("_"))
                except Exception:
                    pass
            info["definition_count"] = total_defs

        return info

    def _check_package_metadata(self, report: ExternalizationReport) -> None:
        """Check pyproject.toml has required metadata."""
        pyproject_path = self.package_root / "pyproject.toml"

        if not pyproject_path.exists():
            report.checks.append(
                ExternalizationCheck(
                    name="pyproject_exists",
                    category="packaging",
                    passed=False,
                    message="pyproject.toml not found",
                    suggestion="Create pyproject.toml with project metadata",
                )
            )
            return

        try:
            import tomllib

            with open(pyproject_path, "rb") as f:
                pyproject = tomllib.load(f)

            project = pyproject.get("project", {})

            # Check required fields
            required_fields = ["name", "version", "description"]
            for field_name in required_fields:
                if field_name not in project:
                    report.checks.append(
                        ExternalizationCheck(
                            name=f"pyproject_{field_name}",
                            category="packaging",
                            passed=False,
                            message=f"Missing project.{field_name} in pyproject.toml",
                            suggestion=f"Add {field_name} to [project] section",
                        )
                    )
                else:
                    report.checks.append(
                        ExternalizationCheck(
                            name=f"pyproject_{field_name}",
                            category="packaging",
                            passed=True,
                            message=f"project.{field_name} present",
                        )
                    )

            # Check recommended fields (warnings only)
            recommended_fields = ["authors", "readme", "license", "classifiers"]
            for field_name in recommended_fields:
                if field_name not in project:
                    report.checks.append(
                        ExternalizationCheck(
                            name=f"pyproject_{field_name}",
                            category="packaging",
                            passed=False,
                            severity=ValidationSeverity.WARNING,
                            message=f"Missing project.{field_name} in pyproject.toml",
                            suggestion=f"Add {field_name} for better PyPI presence",
                        )
                    )

        except Exception as e:
            report.checks.append(
                ExternalizationCheck(
                    name="pyproject_valid",
                    category="packaging",
                    passed=False,
                    message=f"Error reading pyproject.toml: {e}",
                )
            )

    def _check_documentation(self, report: ExternalizationReport) -> None:
        """Check required documentation is present."""
        # Check for README
        readme_paths = [
            self.package_root / "README.md",
            self.package_root / "README.rst",
            self.package_root / "README",
        ]
        has_readme = any(p.exists() for p in readme_paths)
        report.checks.append(
            ExternalizationCheck(
                name="readme_exists",
                category="documentation",
                passed=has_readme,
                message="README present" if has_readme else "README not found",
                severity=ValidationSeverity.WARNING,
                suggestion="Create README.md with package documentation",
            )
        )

        # Check for CHANGELOG
        changelog_paths = [
            self.package_root / "CHANGELOG.md",
            self.package_root / "CHANGELOG.rst",
            self.package_root / "CHANGELOG",
            self.package_root / "HISTORY.md",
        ]
        has_changelog = any(p.exists() for p in changelog_paths)
        report.checks.append(
            ExternalizationCheck(
                name="changelog_exists",
                category="documentation",
                passed=has_changelog,
                message="CHANGELOG present" if has_changelog else "CHANGELOG not found",
                severity=ValidationSeverity.WARNING,
                suggestion="Create CHANGELOG.md to track versions",
            )
        )

        # Check for schema CLAUDE.md
        claude_path = self.schema_dir / "CLAUDE.md"
        has_claude = claude_path.exists()
        report.checks.append(
            ExternalizationCheck(
                name="schema_docs_exists",
                category="documentation",
                passed=has_claude,
                message="Schema CLAUDE.md present" if has_claude else "Schema CLAUDE.md not found",
                suggestion="Create lightwave/schema/CLAUDE.md",
            )
        )

    def _check_version_consistency(self, report: ExternalizationReport) -> None:
        """Check version consistency across schema files."""
        versions_found: dict[str, list[str]] = {}

        if not self.definitions_dir.exists():
            report.checks.append(
                ExternalizationCheck(
                    name="version_consistency",
                    category="versioning",
                    passed=False,
                    message="Definitions directory not found",
                )
            )
            return

        for yaml_path in self.definitions_dir.glob("**/*.yaml"):
            if yaml_path.name == "__index.yaml":
                continue
            try:
                with open(yaml_path) as f:
                    data = yaml.safe_load(f) or {}
                meta = data.get("_meta", {})
                version = meta.get("version", "")
                if version:
                    rel_path = str(yaml_path.relative_to(self.definitions_dir))
                    if version not in versions_found:
                        versions_found[version] = []
                    versions_found[version].append(rel_path)
            except Exception:
                pass

        if len(versions_found) > 1:
            versions_list = ", ".join(f"v{v}" for v in sorted(versions_found.keys()))
            report.checks.append(
                ExternalizationCheck(
                    name="version_consistency",
                    category="versioning",
                    passed=False,
                    severity=ValidationSeverity.WARNING,
                    message=f"Multiple schema versions found: {versions_list}",
                    suggestion="Consider aligning all schemas to same version",
                )
            )
        else:
            report.checks.append(
                ExternalizationCheck(
                    name="version_consistency",
                    category="versioning",
                    passed=True,
                    message="Schema versions are consistent",
                )
            )

    def _check_no_internal_refs(self, report: ExternalizationReport) -> None:
        """Check for internal-only references that shouldn't be externalized."""
        internal_patterns = [
            r"lightwave-media\.ltd",  # Internal domain
            r"/Users/\w+/",  # Local paths
            r"localhost:\d+",  # Local servers
            r"192\.168\.\d+\.\d+",  # Private IPs
            r"10\.\d+\.\d+\.\d+",  # Private IPs
        ]

        issues_found = []

        if self.definitions_dir.exists():
            for yaml_path in self.definitions_dir.glob("**/*.yaml"):
                try:
                    content = yaml_path.read_text()
                    rel_path = str(yaml_path.relative_to(self.definitions_dir))

                    for pattern in internal_patterns:
                        matches = re.findall(pattern, content)
                        if matches:
                            issues_found.append(f"{rel_path}: {pattern}")
                except Exception:
                    pass

        # Note: internal domain references are expected for this package
        # so we only warn, not error
        if any("localhost" in i or "192.168" in i or "/Users/" in i for i in issues_found):
            report.checks.append(
                ExternalizationCheck(
                    name="no_internal_refs",
                    category="security",
                    passed=False,
                    severity=ValidationSeverity.WARNING,
                    message=f"Found {len(issues_found)} internal references",
                    suggestion="Review and remove local paths and private IPs",
                )
            )
        else:
            report.checks.append(
                ExternalizationCheck(
                    name="no_internal_refs",
                    category="security",
                    passed=True,
                    message="No problematic internal references found",
                )
            )

    def _check_exports(self, report: ExternalizationReport) -> None:
        """Check that key modules are properly exported."""
        # Check __init__.py exists
        init_path = self.schema_dir / "__init__.py"
        if not init_path.exists():
            report.checks.append(
                ExternalizationCheck(
                    name="init_exists",
                    category="exports",
                    passed=False,
                    message="lightwave/schema/__init__.py not found",
                    suggestion="Create __init__.py with public exports",
                )
            )
            return

        # Check key exports are available
        init_content = init_path.read_text()

        expected_exports = [
            "SchemaLoader",  # Loader class
            "CrossReferenceValidator",  # Validator
            "FieldOptions",  # Field options
        ]

        missing_exports = []
        for export in expected_exports:
            if export not in init_content:
                missing_exports.append(export)

        if missing_exports:
            report.checks.append(
                ExternalizationCheck(
                    name="public_exports",
                    category="exports",
                    passed=False,
                    severity=ValidationSeverity.WARNING,
                    message=f"Missing exports: {', '.join(missing_exports)}",
                    suggestion="Add missing exports to __init__.py",
                )
            )
        else:
            report.checks.append(
                ExternalizationCheck(
                    name="public_exports",
                    category="exports",
                    passed=True,
                    message="Key public exports present",
                )
            )


class Command(BaseCommand):
    help = "Validate schema package externalization readiness"

    def add_arguments(self, parser):
        parser.add_argument(
            "--format",
            type=str,
            choices=["text", "json"],
            default="text",
            help="Output format (default: text)",
        )

    def handle(self, *args, **options):
        output_format = options.get("format", "text")
        verbosity = options.get("verbosity", 1)

        validator = ExternalizationValidator()
        report = validator.validate_all()

        if output_format == "json":
            self.stdout.write(json.dumps(report.to_dict(), indent=2))
        else:
            self._output_text(report, verbosity)

        if not report.is_ready:
            raise CommandError(
                f"Package not ready for externalization: {len(report.blocking_issues)} blocking issue(s)"
            )

    def _output_text(self, report: ExternalizationReport, verbosity: int) -> None:
        """Output report in human-readable text format."""
        self.stdout.write("\n" + "=" * 70)
        self.stdout.write("EXTERNALIZATION READINESS REPORT")
        self.stdout.write("=" * 70)

        # Package info
        info = report.package_info
        self.stdout.write(f"\n📦 Package: {info.get('name', 'unknown')}")
        self.stdout.write(f"   Version: {info.get('version', 'unknown')}")
        self.stdout.write(f"   Schemas: {info.get('schema_count', 0)}")
        self.stdout.write(f"   Definitions: {info.get('definition_count', 0)}")

        # Consistency summary
        self.stdout.write("\n" + "-" * 70)
        self.stdout.write("CONSISTENCY CHECKS")
        self.stdout.write("-" * 70)

        consistency = report.consistency_report
        for suite_name, suite in consistency.suites.items():
            if suite.is_valid:
                self.stdout.write(self.style.SUCCESS(f"  ✅ {suite_name}: PASSED"))
            else:
                self.stdout.write(self.style.ERROR(f"  ❌ {suite_name}: {suite.error_count} error(s)"))
                if verbosity > 1:
                    for result in suite.results:
                        if result.severity == ValidationSeverity.ERROR:
                            self.stdout.write(f"      - {result.file}: {result.message}")

        # Externalization checks by category
        self.stdout.write("\n" + "-" * 70)
        self.stdout.write("EXTERNALIZATION CHECKS")
        self.stdout.write("-" * 70)

        categories = {}
        for check in report.checks:
            if check.category not in categories:
                categories[check.category] = []
            categories[check.category].append(check)

        for category, checks in categories.items():
            self.stdout.write(f"\n  📋 {category.upper()}")

            for check in checks:
                if check.passed:
                    self.stdout.write(self.style.SUCCESS(f"    ✅ {check.name}: {check.message}"))
                elif check.severity == ValidationSeverity.ERROR:
                    self.stdout.write(self.style.ERROR(f"    ❌ {check.name}: {check.message}"))
                    if check.suggestion:
                        self.stdout.write(f"       💡 {check.suggestion}")
                else:
                    self.stdout.write(self.style.WARNING(f"    ⚠️  {check.name}: {check.message}"))
                    if check.suggestion and verbosity > 0:
                        self.stdout.write(f"       💡 {check.suggestion}")

        # Final verdict
        self.stdout.write("\n" + "=" * 70)
        self.stdout.write("VERDICT")
        self.stdout.write("=" * 70)

        if report.is_ready:
            self.stdout.write(self.style.SUCCESS("\n🎉 READY FOR EXTERNALIZATION\n"))
            self.stdout.write("   Package can be published to PyPI.")
        else:
            self.stdout.write(self.style.ERROR(f"\n❌ NOT READY - {len(report.blocking_issues)} blocking issue(s)\n"))
            self.stdout.write("   Fix the following issues before publishing:\n")
            for issue in report.blocking_issues:
                self.stdout.write(f"   • [{issue.category}] {issue.name}: {issue.message}")

        if report.warnings:
            self.stdout.write(self.style.WARNING(f"\n   ⚠️  {len(report.warnings)} warning(s) to consider"))

        self.stdout.write("")
