#!/usr/bin/env python3
from __future__ import annotations
"""
Novyx Core - Operator Report Generator
=======================================
Generates daily snapshot reports for operators.

Usage:
    python3 -m tools.operator_report --directory memory --out reports/
"""

from tools.graph import scan_artifacts, find_orphaned_links
from tools.graph_cache import get_index
from tools.sentinel import NovyxSentinel
from typing import Dict, List, Tuple
from pathlib import Path
from datetime import datetime
import sys
import json
import argparse

# Import Novyx tools


def get_last_report_date(reports_dir: Path) -> datetime | None:
    """Get date of most recent report by scanning filenames."""
    if not reports_dir.exists():
        return None

    report_files = list(reports_dir.glob("operator_report_*.md"))
    if not report_files:
        return None

    # Extract dates from filenames
    dates = []
    for report_file in report_files:
        # Format: operator_report_YYYY-MM-DD.md
        try:
            date_str = report_file.stem.replace("operator_report_", "")
            date = datetime.strptime(date_str, "%Y-%m-%d")
            dates.append(date)
        except ValueError:
            continue

    return max(dates) if dates else None


def get_new_artifacts(artifacts: List[Dict], last_report_date: datetime | None) -> List[Dict]:
    """Filter artifacts created since last report."""
    if last_report_date is None:
        return artifacts  # All artifacts are "new"

    new_artifacts = []
    for artifact in artifacts:
        data = artifact['data']

        # Try to get creation timestamp
        timestamp_str = (data.get('createdAt') or
                         data.get('Temporal_Marker') or
                         data.get('novyx:ingestedAt'))

        if timestamp_str:
            try:
                # Parse ISO 8601
                timestamp = datetime.fromisoformat(
                    timestamp_str.replace('Z', '+00:00'))
                # Remove timezone for comparison
                timestamp = timestamp.replace(tzinfo=None)

                if timestamp > last_report_date:
                    new_artifacts.append(artifact)
            except (ValueError, AttributeError):
                # Fall back to file mtime
                try:
                    file_mtime = datetime.fromtimestamp(
                        artifact['path'].stat().st_mtime)
                    if file_mtime > last_report_date:
                        new_artifacts.append(artifact)
                except Exception:
                    pass
        else:
            # No timestamp, use file mtime
            try:
                file_mtime = datetime.fromtimestamp(
                    artifact['path'].stat().st_mtime)
                if file_mtime > last_report_date:
                    new_artifacts.append(artifact)
            except Exception:
                pass

    return new_artifacts


def generate_report(directory: Path, output_dir: Path) -> int:
    """Generate operator report."""

    # Ensure output directory exists
    output_dir.mkdir(parents=True, exist_ok=True)

    # Generate report filename
    today = datetime.now().strftime("%Y-%m-%d")
    report_path = output_dir / f"operator_report_{today}.md"

    print(f"📊 Generating Operator Report")
    print(f"   Directory: {directory}")
    print(f"   Output: {report_path}")
    print()

    # Collect data
    try:
        # Run Sentinel
        sentinel = NovyxSentinel(
            root_dir=directory, verbose=False, enforce_graph=True)
        sentinel.scan_directory(directory)

        passed_count = sum(1 for r in sentinel.results if r.passed)
        failed_count = len(sentinel.results) - passed_count

        # Scan and build graph index (cached)
        artifacts, parse_errors = scan_artifacts(directory)
        index = get_index(directory)
        orphaned = find_orphaned_links(index)

        # Get last report date
        last_report_date = get_last_report_date(output_dir)

        # Get new artifacts
        new_artifacts = get_new_artifacts(artifacts, last_report_date)

    except Exception as e:
        print(f"❌ Failed to collect data: {e}")
        return 1

    # Generate markdown report
    report_lines = []
    report_lines.append(f"# Novyx Core Operator Report")
    report_lines.append(f"")
    report_lines.append(
        f"**Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    report_lines.append(f"**Directory:** `{directory}`")
    report_lines.append(f"")
    report_lines.append(f"---")
    report_lines.append(f"")

    # Sentinel Summary
    report_lines.append(f"## 🛡️ Sentinel Integrity Summary")
    report_lines.append(f"")
    report_lines.append(
        f"- **Total artifacts checked:** {len(sentinel.results)}")
    report_lines.append(f"- **Passed:** {passed_count} ✅")
    report_lines.append(f"- **Failed:** {failed_count} ❌")
    report_lines.append(
        f"- **Overall status:** {'PASS' if failed_count == 0 and not sentinel.graph_errors else 'FAIL'}")
    report_lines.append(f"")

    if failed_count > 0:
        report_lines.append(f"### ⚠️ Failed Artifacts")
        report_lines.append(f"")
        for result in sentinel.results:
            if not result.passed:
                report_lines.append(f"- `{result.file_path}`")
                for error in result.errors[:3]:  # Show first 3 errors
                    report_lines.append(f"  - {error}")
        report_lines.append(f"")

    # Graph Statistics
    report_lines.append(f"## 📊 Graph Statistics")
    report_lines.append(f"")
    report_lines.append(f"- **Total artifacts:** {len(artifacts)}")
    report_lines.append(f"- **Unique IDs:** {len(index['id_to_file'])}")
    report_lines.append(
        f"- **Internal links:** {sum(len(links) for links in index['file_to_links'].values())}")
    report_lines.append(
        f"- **Orphaned links:** {sum(len(orphans) for orphans in orphaned.values())}")
    report_lines.append(f"")

    # Top linked artifacts
    if index['inbound_links']:
        sorted_by_inbound = sorted(
            index['inbound_links'].items(),
            key=lambda x: len(x[1]),
            reverse=True
        )[:5]

        report_lines.append(f"### Top 5 Most-Linked Artifacts")
        report_lines.append(f"")
        for target_id, sources in sorted_by_inbound:
            short_id = target_id[:50] + \
                "..." if len(target_id) > 50 else target_id
            report_lines.append(
                f"- `{short_id}` ({len(sources)} inbound links)")
        report_lines.append(f"")

    # New artifacts since last report
    report_lines.append(f"## 📅 New Artifacts Since Last Report")
    report_lines.append(f"")
    if last_report_date:
        report_lines.append(
            f"**Last report date:** {last_report_date.strftime('%Y-%m-%d')}")
        report_lines.append(f"**New artifacts:** {len(new_artifacts)}")
    else:
        report_lines.append(f"**Last report date:** None (first report)")
        report_lines.append(f"**All artifacts shown:** {len(new_artifacts)}")
    report_lines.append(f"")

    if new_artifacts:
        for artifact in new_artifacts[:20]:  # Show first 20
            data = artifact['data']
            name = data.get('name') or data.get('title') or 'Untitled'
            timestamp = (data.get('createdAt') or
                         data.get('Temporal_Marker') or
                         data.get('novyx:ingestedAt') or
                         'Unknown')
            report_lines.append(f"- `{name}` (created: {timestamp})")

        if len(new_artifacts) > 20:
            report_lines.append(f"- ... and {len(new_artifacts) - 20} more")
    else:
        report_lines.append(f"*No new artifacts since last report*")

    report_lines.append(f"")

    # Risk Notes
    report_lines.append(f"## ⚠️ Risk Notes")
    report_lines.append(f"")

    has_risks = False

    # Orphaned links
    if orphaned:
        has_risks = True
        total_orphans = sum(len(orphans) for orphans in orphaned.values())
        report_lines.append(
            f"### Orphaned Links Detected ({total_orphans} total)")
        report_lines.append(f"")
        report_lines.append(
            f"The following files reference non-existent artifacts:")
        report_lines.append(f"")

        # Top 10 files
        for file_path, orphan_ids in list(orphaned.items())[:10]:
            report_lines.append(
                f"**`{file_path}`** ({len(orphan_ids)} orphans):")
            for orphan_id in list(orphan_ids)[:5]:  # Show first 5 per file
                report_lines.append(f"  - `{orphan_id}`")
            if len(orphan_ids) > 5:
                report_lines.append(f"  - ... and {len(orphan_ids) - 5} more")
            report_lines.append(f"")

        if len(orphaned) > 10:
            report_lines.append(
                f"*... and {len(orphaned) - 10} more files with orphans*")
            report_lines.append(f"")

        report_lines.append(
            f"**Action:** Run `python3 -m tools.cli repair --directory {directory} --dry-run`")
        report_lines.append(f"")

    # Parse errors
    if parse_errors:
        has_risks = True
        report_lines.append(f"### Parse Errors ({len(parse_errors)} files)")
        report_lines.append(f"")
        report_lines.append(f"The following files failed to parse:")
        report_lines.append(f"")
        for error in parse_errors[:10]:
            report_lines.append(f"- {error}")
        if len(parse_errors) > 10:
            report_lines.append(f"- ... and {len(parse_errors) - 10} more")
        report_lines.append(f"")

    # Graph errors from Sentinel
    if sentinel.graph_errors:
        has_risks = True
        report_lines.append(f"### Graph Integrity Violations")
        report_lines.append(f"")
        for error in sentinel.graph_errors:
            report_lines.append(f"- {error}")
        report_lines.append(f"")

    if not has_risks:
        report_lines.append(
            f"✅ **No risks detected.** All integrity checks passed.")
        report_lines.append(f"")

    # API Usage Metrics (Phase 6, Days 6-7)
    report_lines.append(f"## 🌐 API Usage Metrics")
    report_lines.append(f"")

    try:
        # Import logging spoke to parse logs
        from spokes.request_logger import parse_logs

        logs_dir = directory / "logs"
        api_metrics = parse_logs(logs_dir)

        if api_metrics["total_requests"] > 0:
            report_lines.append(
                f"**Total API Requests:** {api_metrics['total_requests']}")
            report_lines.append(
                f"**Average Response Time:** {api_metrics['average_duration_ms']}ms")
            report_lines.append(
                f"**Error Count:** {api_metrics['error_count']} ({round(api_metrics['error_count'] / api_metrics['total_requests'] * 100, 1)}%)")
            report_lines.append(f"")

            # Top endpoints
            if api_metrics["top_endpoints"]:
                report_lines.append(f"### Top 5 API Endpoints")
                report_lines.append(f"")
                for item in api_metrics["top_endpoints"]:
                    report_lines.append(
                        f"- `{item['endpoint']}` - {item['count']} requests")
                report_lines.append(f"")

            # Requests by status
            if api_metrics["requests_by_status"]:
                report_lines.append(f"### Requests by Status Code")
                report_lines.append(f"")
                for status_code, count in sorted(api_metrics["requests_by_status"].items()):
                    emoji = "✅" if status_code < 400 else "⚠️" if status_code < 500 else "❌"
                    report_lines.append(
                        f"- {emoji} **{status_code}**: {count} requests")
                report_lines.append(f"")
        else:
            report_lines.append(f"*No API requests logged yet*")
            report_lines.append(f"")

    except ImportError:
        report_lines.append(
            f"*API logging not available (structlog not installed)*")
        report_lines.append(f"")
    except Exception as e:
        report_lines.append(f"*Failed to parse API logs: {e}*")
        report_lines.append(f"")

    # Phase 8: Query Metrics (Semantic Search Usage)
    report_lines.append(f"## 🔍 Query Metrics (Phase 8)")
    report_lines.append(f"")

    try:
        # Analyze semantic search usage from logs or artifacts
        from collections import Counter

        query_count = 0
        search_terms = []
        avg_scores = []

        # Parse logs for query operations if available
        logs_dir = directory / "logs"
        if logs_dir.exists():
            for log_file in logs_dir.glob("*.jsonld"):
                try:
                    with open(log_file, 'r') as f:
                        log_data = json.load(f)

                        # Check if this is a query/search operation
                        if "query" in log_data.get("articleBody", "").lower() or \
                           "search" in log_data.get("articleBody", "").lower():
                            query_count += 1

                            # Extract search term if present (basic heuristic)
                            body = log_data.get("articleBody", "")
                            if "search_term=" in body or "searchTerm:" in body:
                                # This is a simplified extraction
                                query_count += 1
                except Exception:
                    continue

        # Count artifacts by category to show most queried domains
        category_counts = Counter()
        for artifact in artifacts:
            cat = artifact.get("category", "unknown")
            category_counts[cat] += 1

        if query_count > 0:
            report_lines.append(f"**Total Semantic Searches:** {query_count}")
            report_lines.append(f"")

            if search_terms:
                report_lines.append(f"### Top Search Terms")
                report_lines.append(f"")
                term_counts = Counter(search_terms)
                for term, count in term_counts.most_common(5):
                    report_lines.append(f"- \"{term}\" - {count} searches")
                report_lines.append(f"")

            if avg_scores:
                avg_score = sum(avg_scores) / len(avg_scores)
                report_lines.append(
                    f"**Average Similarity Score:** {avg_score:.3f}")
                report_lines.append(f"")
        else:
            report_lines.append(f"*No semantic searches logged yet*")
            report_lines.append(f"")

        # Show artifact distribution (helps understand what can be queried)
        if category_counts:
            report_lines.append(f"### Queryable Artifact Distribution")
            report_lines.append(f"")
            for category, count in category_counts.most_common(7):
                percentage = (count / len(artifacts) * 100) if artifacts else 0
                report_lines.append(
                    f"- **{category}**: {count} artifacts ({percentage:.1f}%)")
            report_lines.append(f"")

        # Embeddings coverage
        embedded_count = sum(1 for a in artifacts if a.get(
            "embedding") or a.get("novyx:embedding"))
        if len(artifacts) > 0:
            embed_percentage = (embedded_count / len(artifacts)) * 100
            report_lines.append(
                f"**Embedding Coverage:** {embedded_count}/{len(artifacts)} ({embed_percentage:.1f}%)")
            if embed_percentage < 100:
                report_lines.append(
                    f"  ⚠️  *{len(artifacts) - embedded_count} artifacts without embeddings (re-ingest to enable semantic search)*")
            report_lines.append(f"")

    except Exception as e:
        report_lines.append(f"*Failed to generate query metrics: {e}*")
        report_lines.append(f"")

    # Phase 9: Tenant Metrics (Multi-Tenant Analytics)
    report_lines.append(f"## 🏢 Tenant Metrics (Phase 9)")
    report_lines.append(f"")

    try:
        # Analyze tenant distribution
        tenant_counts = {}
        tenant_artifacts = {}

        for artifact in artifacts:
            tenant_id = artifact.get(
                "tenant_id") or artifact.get("novyx:tenantId")
            if tenant_id:
                tenant_counts[tenant_id] = tenant_counts.get(tenant_id, 0) + 1
                if tenant_id not in tenant_artifacts:
                    tenant_artifacts[tenant_id] = []
                tenant_artifacts[tenant_id].append(artifact)
            else:
                # Count artifacts without tenant (global/legacy)
                tenant_counts["_global_"] = tenant_counts.get(
                    "_global_", 0) + 1

        if tenant_counts:
            total_tenants = len([t for t in tenant_counts if t != "_global_"])
            report_lines.append(f"**Total Tenants:** {total_tenants}")
            report_lines.append(
                f"**Total Tenant Artifacts:** {sum(count for tid, count in tenant_counts.items() if tid != '_global_')}")

            if "_global_" in tenant_counts:
                report_lines.append(
                    f"**Global/Legacy Artifacts:** {tenant_counts['_global_']}")
            report_lines.append(f"")

            # Top tenants by artifact count
            if total_tenants > 0:
                report_lines.append(f"### Top Tenants by Artifact Count")
                report_lines.append(f"")

                tenant_list = [
                    (tid, count) for tid, count in tenant_counts.items() if tid != "_global_"]
                tenant_list.sort(key=lambda x: x[1], reverse=True)

                for tenant_id, count in tenant_list[:10]:  # Top 10 tenants
                    percentage = (count / len(artifacts) *
                                  100) if artifacts else 0
                    report_lines.append(
                        f"- **{tenant_id}**: {count} artifacts ({percentage:.1f}%)")
                report_lines.append(f"")

            # Tenant activity analysis (check for recent activity)
            active_tenants = []
            for tenant_id, tenant_arts in tenant_artifacts.items():
                # Get latest artifact timestamp for this tenant
                latest_time = None
                for art in tenant_arts:
                    timestamp = art.get("createdAt") or art.get(
                        "updatedAt") or art.get("Temporal_Marker")
                    if timestamp:
                        if latest_time is None or timestamp > latest_time:
                            latest_time = timestamp

                if latest_time:
                    active_tenants.append(
                        (tenant_id, latest_time, len(tenant_arts)))

            if active_tenants:
                active_tenants.sort(key=lambda x: x[1], reverse=True)
                report_lines.append(f"### Most Recently Active Tenants")
                report_lines.append(f"")
                for tenant_id, last_activity, count in active_tenants[:5]:
                    report_lines.append(
                        f"- **{tenant_id}**: Last activity {last_activity} ({count} artifacts)")
                report_lines.append(f"")

            # Data isolation status
            report_lines.append(f"### Data Isolation Status")
            report_lines.append(f"")

            # Check if tenants have dedicated subdirectories
            tenants_dir = directory / "tenants"
            if tenants_dir.exists():
                tenant_defs = list(tenants_dir.rglob("*.jsonld"))
                report_lines.append(
                    f"✅ **Tenant Definitions Found:** {len(tenant_defs)}")

                # Check for tenant subdirectories in artifact directories
                isolation_check = True
                for subdir in directory.iterdir():
                    if subdir.is_dir() and subdir.name not in ["tenants", "logs", "stubs"]:
                        # Check if this directory has tenant subdirectories
                        tenant_subdirs = [
                            d for d in subdir.iterdir() if d.is_dir()]
                        if tenant_subdirs:
                            report_lines.append(
                                f"✅ **{subdir.name}/**: {len(tenant_subdirs)} tenant subdirectories")

                report_lines.append(f"")
            else:
                report_lines.append(f"*No tenant definitions found*")
                report_lines.append(f"")
        else:
            report_lines.append(
                f"*No multi-tenant artifacts found (all artifacts are global/legacy)*")
            report_lines.append(f"")

    except Exception as e:
        report_lines.append(f"*Failed to generate tenant metrics: {e}*")
        report_lines.append(f"")

    # Phase 10: Version Metrics (Artifact Versioning Analytics)
    report_lines.append(f"## 📦 Version Metrics (Phase 10)")
    report_lines.append(f"")

    try:
        # Analyze version history
        versions_dir = directory / "versions"
        version_stats = {
            "total_versions": 0,
            "artifacts_with_versions": 0,
            "avg_versions_per_artifact": 0.0,
            "max_versions": 0,
            "most_versioned_artifacts": [],
            "recent_rollbacks": 0,
            "version_storage_size": 0
        }

        if versions_dir.exists():
            # Scan version directories
            artifact_version_counts = {}

            for artifact_dir in versions_dir.iterdir():
                if not artifact_dir.is_dir():
                    continue

                # Count versions for this artifact
                version_files = list(artifact_dir.glob("*.jsonld"))
                if version_files:
                    artifact_id = f"urn:uuid:{artifact_dir.name}"
                    version_count = len(version_files)
                    artifact_version_counts[artifact_id] = version_count
                    version_stats["total_versions"] += version_count

                    # Track storage size
                    for vfile in version_files:
                        try:
                            version_stats["version_storage_size"] += vfile.stat().st_size
                        except:
                            pass

            version_stats["artifacts_with_versions"] = len(
                artifact_version_counts)

            if artifact_version_counts:
                version_stats["avg_versions_per_artifact"] = version_stats["total_versions"] / len(
                    artifact_version_counts)
                version_stats["max_versions"] = max(
                    artifact_version_counts.values())

                # Top 5 most versioned artifacts
                sorted_artifacts = sorted(
                    artifact_version_counts.items(), key=lambda x: x[1], reverse=True)[:5]
                version_stats["most_versioned_artifacts"] = sorted_artifacts

            # Display metrics
            if version_stats["total_versions"] > 0:
                report_lines.append(
                    f"**Total Versions:** {version_stats['total_versions']}")
                report_lines.append(
                    f"**Artifacts with Version History:** {version_stats['artifacts_with_versions']}")
                report_lines.append(
                    f"**Average Versions per Artifact:** {version_stats['avg_versions_per_artifact']:.1f}")
                report_lines.append(
                    f"**Maximum Versions (single artifact):** {version_stats['max_versions']}")

                # Storage size
                storage_mb = version_stats["version_storage_size"] / \
                    (1024 * 1024)
                report_lines.append(
                    f"**Version Storage Size:** {storage_mb:.2f} MB")
                report_lines.append(f"")

                # Most versioned artifacts
                if version_stats["most_versioned_artifacts"]:
                    report_lines.append(
                        f"### Top 5 Most Versioned Artifacts")
                    report_lines.append(f"")
                    for artifact_id, count in version_stats["most_versioned_artifacts"]:
                        short_id = artifact_id.replace(
                            "urn:uuid:", "")[:16] + "..."
                        report_lines.append(
                            f"- `{short_id}` - {count} versions")
                    report_lines.append(f"")

                # Check for recent rollbacks (v1.0 timestamp > latest version timestamp)
                # This is a heuristic check - actual rollbacks detected by change_type field
                rollback_count = 0
                for artifact_dir in versions_dir.iterdir():
                    if not artifact_dir.is_dir():
                        continue

                    version_files = sorted(
                        artifact_dir.glob("*.jsonld"), key=lambda f: f.stat().st_mtime, reverse=True)
                    if len(version_files) >= 2:
                        try:
                            with open(version_files[0], 'r') as f:
                                latest_version = json.load(f)
                            if latest_version.get("change_type") == "rollback":
                                rollback_count += 1
                        except:
                            pass

                if rollback_count > 0:
                    report_lines.append(
                        f"**Recent Rollbacks Detected:** {rollback_count}")
                    report_lines.append(
                        f"  ℹ️  *Artifacts have been rolled back to previous versions*")
                    report_lines.append(f"")

                # Version health check
                report_lines.append(f"### Version Health")
                report_lines.append(f"")
                if version_stats["max_versions"] > 20:
                    report_lines.append(
                        f"⚠️  *Some artifacts have over 20 versions - consider periodic archiving*")
                elif version_stats["max_versions"] > 10:
                    report_lines.append(
                        f"ℹ️  *Healthy version counts (max: {version_stats['max_versions']})*")
                else:
                    report_lines.append(
                        f"✅ *Optimal version counts (max: {version_stats['max_versions']})*")
                report_lines.append(f"")

            else:
                report_lines.append(f"*No version history found*")
                report_lines.append(
                    f"  ℹ️  *Update artifacts using `persist_artifact()` to enable automatic versioning*")
                report_lines.append(f"")
        else:
            report_lines.append(f"*Versions directory not found*")
            report_lines.append(
                f"  ℹ️  *Version tracking starts automatically when artifacts are updated*")
            report_lines.append(f"")

    except Exception as e:
        report_lines.append(f"*Failed to generate version metrics: {e}*")
        report_lines.append(f"")

    # Phase 11: Export/Import Metrics
    report_lines.append(f"## 📤 Export/Import Metrics (Phase 11)")
    report_lines.append(f"")

    try:
        from tools.export import check_dependencies, ExportFormat, RDFLIB_AVAILABLE
        from tools.import_data import ImportFormat

        # Check export capabilities
        report_lines.append(f"### Export Capabilities")
        report_lines.append(f"")

        formats_available = []
        formats_unavailable = []

        # JSON-LD always available
        formats_available.append("JSON-LD")

        # RDF/Turtle
        if RDFLIB_AVAILABLE:
            formats_available.append("RDF/Turtle")
        else:
            formats_unavailable.append("RDF/Turtle (install rdflib)")

        # Neo4j Cypher (always available - generates file)
        formats_available.append("Neo4j Cypher")

        report_lines.append(f"**Available Formats:** {', '.join(formats_available)}")
        if formats_unavailable:
            report_lines.append(f"**Unavailable Formats:** {', '.join(formats_unavailable)}")
        report_lines.append(f"")

        # Export readiness analysis
        report_lines.append(f"### Export Readiness")
        report_lines.append(f"")

        # Count artifacts suitable for export
        exportable_count = 0
        has_sameAs_count = 0
        has_embedding_count = 0

        for artifact in artifacts:
            data = artifact.get('data', artifact)
            exportable_count += 1

            # Check for sameAs (external links)
            same_as = data.get('sameAs', [])
            if same_as:
                has_sameAs_count += 1

            # Check for embeddings
            if data.get('novyx:embedding') or data.get('embedding'):
                has_embedding_count += 1

        report_lines.append(f"**Exportable Artifacts:** {exportable_count}")
        report_lines.append(f"**With External Links (sameAs):** {has_sameAs_count}")
        report_lines.append(f"**With Embeddings:** {has_embedding_count}")
        report_lines.append(f"")

        # Estimate export sizes
        report_lines.append(f"### Estimated Export Sizes")
        report_lines.append(f"")

        # Calculate rough sizes
        total_size_bytes = 0
        for artifact in artifacts:
            try:
                artifact_path = artifact.get('path')
                if artifact_path and artifact_path.exists():
                    total_size_bytes += artifact_path.stat().st_size
            except:
                pass

        if total_size_bytes > 0:
            size_kb = total_size_bytes / 1024
            size_mb = size_kb / 1024

            # JSON-LD: roughly same size
            report_lines.append(f"- **JSON-LD:** ~{size_mb:.2f} MB")

            # RDF: typically 1.2-1.5x due to triple format
            rdf_estimate = size_mb * 1.3
            report_lines.append(f"- **RDF/Turtle:** ~{rdf_estimate:.2f} MB (estimated)")

            # Neo4j Cypher: roughly same, but without embeddings
            embedding_ratio = has_embedding_count / exportable_count if exportable_count > 0 else 0
            cypher_estimate = size_mb * (1 - embedding_ratio * 0.7)  # Embeddings are ~70% of size
            report_lines.append(f"- **Neo4j Cypher:** ~{cypher_estimate:.2f} MB (without embeddings)")
        else:
            report_lines.append(f"*Unable to estimate export sizes*")
        report_lines.append(f"")

        # CLI hints
        report_lines.append(f"### Quick Export Commands")
        report_lines.append(f"")
        report_lines.append(f"```bash")
        report_lines.append(f"# Export to JSON-LD")
        report_lines.append(f"python3 -m tools.cli export --format jsonld --output backup.jsonld --directory {directory}")
        report_lines.append(f"")
        report_lines.append(f"# Export to Neo4j")
        report_lines.append(f"python3 -m tools.cli export --format neo4j --output import.cypher --directory {directory}")
        report_lines.append(f"```")
        report_lines.append(f"")

    except ImportError as e:
        report_lines.append(f"*Export module not available: {e}*")
        report_lines.append(f"")
    except Exception as e:
        report_lines.append(f"*Failed to generate export metrics: {e}*")
        report_lines.append(f"")

    # Phase 14: Federation Metrics
    report_lines.append(f"## 🌐 Federation Metrics (Phase 14)")
    report_lines.append(f"")

    try:
        from tools.federation import federation_status, get_remote_refs, REQUESTS_AVAILABLE

        fed_status = federation_status()

        report_lines.append(f"**Total RemoteRefs:** {fed_status['total']}")
        report_lines.append(f"**HTTP Client (requests):** {'Available' if fed_status['requests_available'] else 'Not Available'}")
        report_lines.append(f"")

        if fed_status['total'] > 0:
            # Status breakdown
            report_lines.append(f"### Sync Status Distribution")
            report_lines.append(f"")
            for status_name, count in fed_status['by_status'].items():
                if count > 0:
                    emoji = "✅" if status_name == "synced" else "⏳" if status_name == "pending" else "⚠️" if status_name == "conflict" else "📅"
                    report_lines.append(f"- {emoji} **{status_name}:** {count}")
            report_lines.append(f"")

            # Direction breakdown
            report_lines.append(f"### Sync Direction Distribution")
            report_lines.append(f"")
            for direction, count in fed_status['by_direction'].items():
                if count > 0:
                    emoji = "⬇️" if direction == "pull" else "⬆️" if direction == "push" else "↔️"
                    report_lines.append(f"- {emoji} **{direction}:** {count}")
            report_lines.append(f"")

            # Remote instances
            if fed_status['remote_instances']:
                report_lines.append(f"### Connected Remote Instances ({len(fed_status['remote_instances'])})")
                report_lines.append(f"")
                for instance in fed_status['remote_instances'][:10]:
                    report_lines.append(f"- `{instance}`")
                if len(fed_status['remote_instances']) > 10:
                    report_lines.append(f"- ... and {len(fed_status['remote_instances']) - 10} more")
                report_lines.append(f"")

            # Health check
            report_lines.append(f"### Federation Health")
            report_lines.append(f"")

            conflicts = fed_status['by_status'].get('conflict', 0)
            stale = fed_status['by_status'].get('stale', 0)
            pending = fed_status['by_status'].get('pending', 0)

            if conflicts > 0:
                report_lines.append(f"⚠️  **{conflicts} conflict(s)** need manual resolution")
            if stale > 0:
                report_lines.append(f"📅 **{stale} stale ref(s)** need re-sync")
            if pending > 0:
                report_lines.append(f"⏳ **{pending} pending ref(s)** awaiting sync")
            if conflicts == 0 and stale == 0 and pending == 0:
                report_lines.append(f"✅ All references are synced")
            report_lines.append(f"")

            # CLI hints
            report_lines.append(f"### Quick Federation Commands")
            report_lines.append(f"")
            report_lines.append(f"```bash")
            report_lines.append(f"# Check federation status")
            report_lines.append(f"python3 -m tools.cli sync status")
            report_lines.append(f"")
            report_lines.append(f"# Sync pending refs")
            report_lines.append(f"python3 -m tools.cli sync run")
            report_lines.append(f"```")
            report_lines.append(f"")

        else:
            report_lines.append(f"*No federation references found*")
            report_lines.append(f"  ℹ️  *Create RemoteRefs to enable distributed artifact sync*")
            report_lines.append(f"")

    except ImportError as e:
        report_lines.append(f"*Federation module not available: {e}*")
        report_lines.append(f"")
    except Exception as e:
        report_lines.append(f"*Failed to generate federation metrics: {e}*")
        report_lines.append(f"")

    # Release Metrics (v1.0.0)
    report_lines.append(f"## 📦 Release Metrics (v1.0.0)")
    report_lines.append(f"")

    try:
        import subprocess
        from pathlib import Path as P

        # Get version from pyproject.toml
        pyproject_path = P(__file__).parent.parent / "pyproject.toml"
        version = "unknown"
        if pyproject_path.exists():
            with open(pyproject_path, 'r') as f:
                for line in f:
                    if line.startswith('version = '):
                        version = line.split('"')[1]
                        break

        report_lines.append(f"**Package Version:** {version}")

        # Check for dist files
        dist_dir = P(__file__).parent.parent / "dist"
        if dist_dir.exists():
            wheel_files = list(dist_dir.glob("*.whl"))
            tar_files = list(dist_dir.glob("*.tar.gz"))

            if wheel_files or tar_files:
                report_lines.append(f"**Build Status:** Ready for distribution")

                if wheel_files:
                    wheel_size = sum(f.stat().st_size for f in wheel_files) / 1024
                    report_lines.append(f"**Wheel Size:** {wheel_size:.1f} KB")

                if tar_files:
                    tar_size = sum(f.stat().st_size for f in tar_files) / 1024
                    report_lines.append(f"**Source Dist:** {tar_size:.1f} KB")
            else:
                report_lines.append(f"**Build Status:** Not built (run `python -m build`)")
        else:
            report_lines.append(f"**Build Status:** Not built")

        report_lines.append(f"")

        # Git status
        try:
            result = subprocess.run(
                ["git", "describe", "--tags", "--abbrev=0"],
                capture_output=True, text=True, cwd=str(P(__file__).parent.parent),
                timeout=5
            )
            if result.returncode == 0:
                latest_tag = result.stdout.strip()
                report_lines.append(f"**Latest Git Tag:** {latest_tag}")

            # Check if on main branch
            result = subprocess.run(
                ["git", "branch", "--show-current"],
                capture_output=True, text=True, cwd=str(P(__file__).parent.parent),
                timeout=5
            )
            if result.returncode == 0:
                branch = result.stdout.strip()
                report_lines.append(f"**Current Branch:** {branch}")

        except Exception:
            pass

        report_lines.append(f"")

        # Test coverage estimate
        tests_dir = P(__file__).parent.parent / "tests"
        if tests_dir.exists():
            test_files = list(tests_dir.glob("test_*.py"))
            test_count = 0
            for tf in test_files:
                with open(tf, 'r') as f:
                    content = f.read()
                    test_count += content.count("def test_")

            report_lines.append(f"### Test Coverage")
            report_lines.append(f"")
            report_lines.append(f"**Test Files:** {len(test_files)}")
            report_lines.append(f"**Test Functions:** {test_count}")
            report_lines.append(f"")

        # Schema version
        schema_path = P(__file__).parent.parent / "CORE_SCHEMA.jsonld"
        if schema_path.exists():
            with open(schema_path, 'r') as f:
                schema_content = f.read()
                if '"version":' in schema_content:
                    import re
                    match = re.search(r'"version":\s*"([^"]+)"', schema_content)
                    if match:
                        schema_version = match.group(1)
                        report_lines.append(f"**Schema Version:** {schema_version}")

        report_lines.append(f"")

        # Release checklist
        report_lines.append(f"### Release Checklist")
        report_lines.append(f"")

        checklist = [
            ("pyproject.toml version 1.0.0", version == "1.0.0"),
            ("Build artifacts exist", dist_dir.exists() and list(dist_dir.glob("*.whl"))),
            ("Tests present", tests_dir.exists() and test_count > 100),
            ("CHANGELOG exists", (P(__file__).parent.parent / "CHANGELOG.md").exists()),
            ("README exists", (P(__file__).parent.parent / "README.md").exists()),
        ]

        for item, status in checklist:
            emoji = "✅" if status else "❌"
            report_lines.append(f"- {emoji} {item}")

        report_lines.append(f"")

    except Exception as e:
        report_lines.append(f"*Failed to generate release metrics: {e}*")
        report_lines.append(f"")

    # Footer
    report_lines.append(f"---")
    report_lines.append(f"")
    report_lines.append(f"*Generated by Novyx Core Operator Report*  ")
    report_lines.append(
        f"*Repository: https://github.com/novyxlabs/novyx-core*")

    # Write report
    try:
        with open(report_path, 'w') as f:
            f.write('\n'.join(report_lines))

        print(f"✅ Report generated: {report_path}")
        print(f"   Total artifacts: {len(artifacts)}")
        print(f"   New since last report: {len(new_artifacts)}")
        if orphaned:
            print(
                f"   ⚠️  Orphaned links: {sum(len(o) for o in orphaned.values())}")

        return 0

    except Exception as e:
        print(f"❌ Failed to write report: {e}")
        return 1


def main():
    """Main entrypoint."""
    parser = argparse.ArgumentParser(
        description="Novyx Core Operator Report Generator"
    )
    parser.add_argument("--directory", required=True,
                        help="Directory to analyze")
    parser.add_argument("--out", required=True,
                        help="Output directory for reports")

    args = parser.parse_args()

    directory = Path(args.directory)
    output_dir = Path(args.out)

    if not directory.exists():
        print(f"❌ Directory not found: {directory}")
        return 1

    return generate_report(directory, output_dir)


if __name__ == "__main__":
    sys.exit(main())
