"""Tag governance service for dynamic tag lifecycle management (Phase 5).

Implements tag promotion workflow and trending analysis per Spec Section 4.4.
"""

import json
from pathlib import Path
from typing import Any, Optional

import structlog
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession

from src.extraction_v2.taxonomy_loader import TaxonomyLoader

logger = structlog.get_logger(__name__)


class DynamicTagAnalyzer:
    """Analyzes dynamic tag usage to identify trending tags for promotion.

    Phase 5 Enhancement: Trending tag detection per Spec Section 4.4.2.
    """

    def __init__(self, session: AsyncSession):
        """Initialize dynamic tag analyzer.

        Args:
            session: Database session
        """
        self.session = session
        self.logger = structlog.get_logger(__name__)

    async def get_trending_tags(
        self,
        min_usage: int = 50,
        min_documents: int = 3,
        namespace: Optional[str] = None,
    ) -> list[dict[str, Any]]:
        """Get trending dynamic tags that are candidates for promotion.

        Args:
            min_usage: Minimum number of times tag must be used
            min_documents: Minimum number of documents tag must appear in
            namespace: Optional namespace filter (e.g., "topic", "entity")

        Returns:
            List of dicts with tag info:
            {
                "name": "topic:gdpr",
                "usage_count": 127,
                "document_count": 8,
                "verified_ratio": 0.85,
                "first_seen": "2026-01-15",
                "last_seen": "2026-01-22"
            }

        Note:
            Spec Section 4.4.2: Weekly batch job identifies trending tags
        """
        # Build namespace filter
        namespace_filter = ""
        if namespace:
            namespace_filter = f"AND tag->>'name' LIKE '{namespace}:%'"

        # Query to aggregate tag statistics
        query = text(f"""
            WITH tag_stats AS (
                SELECT
                    tag->>'name' as tag_name,
                    COUNT(*) as usage_count,
                    COUNT(DISTINCT document_id) as document_count,
                    SUM(CASE WHEN tag->>'status' = 'verified' THEN 1 ELSE 0 END)::float / COUNT(*) as verified_ratio,
                    MIN(created_at) as first_seen,
                    MAX(created_at) as last_seen
                FROM extracted_records,
                     jsonb_array_elements(tags) as tag
                WHERE tag->>'source' IN ('ai', 'human')
                  AND tag->>'status' != 'deleted'
                  {namespace_filter}
                GROUP BY tag->>'name'
            )
            SELECT
                tag_name,
                usage_count,
                document_count,
                verified_ratio,
                first_seen,
                last_seen
            FROM tag_stats
            WHERE usage_count >= :min_usage
              AND document_count >= :min_documents
            ORDER BY usage_count DESC
            LIMIT 100
        """)

        result = await self.session.execute(
            query,
            {"min_usage": min_usage, "min_documents": min_documents},
        )

        trending = []
        for row in result:
            trending.append(
                {
                    "name": row[0],
                    "usage_count": row[1],
                    "document_count": row[2],
                    "verified_ratio": float(row[3]) if row[3] else 0.0,
                    "first_seen": row[4].strftime("%Y-%m-%d") if row[4] else None,
                    "last_seen": row[5].strftime("%Y-%m-%d") if row[5] else None,
                }
            )

        self.logger.info(
            "trending_tags_analyzed",
            count=len(trending),
            min_usage=min_usage,
            min_documents=min_documents,
            namespace=namespace,
        )

        return trending

    async def get_tag_usage_stats(self) -> dict[str, Any]:
        """Get overall tag usage statistics.

        Returns:
            Dict with overall statistics:
            {
                "total_tags": 1523,
                "by_namespace": {"req": 456, "topic": 789, ...},
                "by_status": {"verified": 1200, "pending": 323},
                "dynamic_tags": 789
            }
        """
        # Query namespace distribution
        namespace_query = text("""
            SELECT
                split_part(tag->>'name', ':', 1) as namespace,
                COUNT(*) as count
            FROM extracted_records,
                 jsonb_array_elements(tags) as tag
            WHERE tag->>'status' != 'deleted'
            GROUP BY namespace
            ORDER BY count DESC
        """)

        namespace_result = await self.session.execute(namespace_query)
        by_namespace = {row[0]: row[1] for row in namespace_result}

        # Query status distribution
        status_query = text("""
            SELECT
                tag->>'status' as status,
                COUNT(*) as count
            FROM extracted_records,
                 jsonb_array_elements(tags) as tag
            GROUP BY status
            ORDER BY count DESC
        """)

        status_result = await self.session.execute(status_query)
        by_status = {row[0]: row[1] for row in status_result}

        # Calculate dynamic tags (topic, entity, tech, domain)
        dynamic_namespaces = ["topic", "entity", "tech", "domain"]
        dynamic_count = sum(
            count for ns, count in by_namespace.items() if ns in dynamic_namespaces
        )

        total_tags = sum(by_status.values())

        return {
            "total_tags": total_tags,
            "by_namespace": by_namespace,
            "by_status": by_status,
            "dynamic_tags": dynamic_count,
        }


class TagPromotionService:
    """Manages promotion of dynamic tags to standard taxonomy.

    Phase 5 Enhancement: Tag promotion lifecycle per Spec Section 4.4.3.
    """

    def __init__(self, session: AsyncSession, taxonomy_loader: TaxonomyLoader):
        """Initialize tag promotion service.

        Args:
            session: Database session
            taxonomy_loader: Taxonomy loader for validation
        """
        self.session = session
        self.taxonomy_loader = taxonomy_loader
        self.logger = structlog.get_logger(__name__)

    async def promote_tag(
        self,
        old_tag: str,
        new_tag: str,
        profile: Optional[str] = None,
    ) -> tuple[bool, int, Optional[str]]:
        """Promote a dynamic tag to standard taxonomy.

        Args:
            old_tag: Current dynamic tag (e.g., "topic:gdpr")
            new_tag: New canonical tag (e.g., "compliance:regulatory:gdpr")
            profile: Optional profile to add tag to (e.g., "fintech")

        Returns:
            Tuple of (success, records_updated, error_message)

        Note:
            Spec Section 4.4.3: Promotion workflow
            1. Admin maps dynamic tag to canonical namespace
            2. Add to standard_tags.json or profile
            3. Batch migration updates all records
            4. Re-index affected records (future enhancement)
        """
        # Validate new tag format
        if ":" not in new_tag:
            return False, 0, "New tag must be in namespace:value format"

        namespace, value = new_tag.split(":", 1)

        # Check if new tag is valid
        is_valid, status, reason = self.taxonomy_loader.is_valid_tag(new_tag)
        if not is_valid and status == "rejected":
            return False, 0, f"Invalid new tag: {reason}"

        # Check if old tag exists in database
        check_query = text("""
            SELECT COUNT(*)
            FROM extracted_records,
                 jsonb_array_elements(tags) as tag
            WHERE tag->>'name' = :old_tag
              AND tag->>'status' != 'deleted'
        """)

        result = await self.session.execute(check_query, {"old_tag": old_tag})
        old_count = result.scalar()

        if old_count == 0:
            return False, 0, f"Tag '{old_tag}' not found in database"

        # Perform batch update - rename tag in all records
        update_query = text("""
            UPDATE extracted_records
            SET tags = (
                SELECT jsonb_agg(
                    CASE
                        WHEN tag->>'name' = :old_tag
                        THEN jsonb_set(
                            jsonb_set(
                                tag,
                                '{name}',
                                to_jsonb(:new_tag::text)
                            ),
                            '{history}',
                            COALESCE(tag->'history', '[]'::jsonb) ||
                            jsonb_build_array(
                                jsonb_build_object(
                                    'action', 'promoted',
                                    'by', 'admin',
                                    'ts', extract(epoch from now())::bigint,
                                    'old_value', :old_tag
                                )
                            )
                        )
                        ELSE tag
                    END
                )
                FROM jsonb_array_elements(tags) as tag
            )
            WHERE EXISTS (
                SELECT 1
                FROM jsonb_array_elements(tags) as tag
                WHERE tag->>'name' = :old_tag
                  AND tag->>'status' != 'deleted'
            )
        """)

        result = await self.session.execute(
            update_query,
            {"old_tag": old_tag, "new_tag": new_tag},
        )

        await self.session.commit()

        records_updated = result.rowcount

        self.logger.info(
            "tag_promoted",
            old_tag=old_tag,
            new_tag=new_tag,
            records_updated=records_updated,
            profile=profile,
        )

        return True, records_updated, None

    def add_to_taxonomy_file(
        self,
        tag: str,
        description: str,
        examples: list[str],
        profile: Optional[str] = None,
    ) -> tuple[bool, Optional[str]]:
        """Add promoted tag to taxonomy configuration file.

        Args:
            tag: Tag in format "namespace:value"
            description: Tag description
            examples: List of example usages
            profile: Optional profile name (None for standard taxonomy)

        Returns:
            Tuple of (success, error_message)

        Note:
            This updates the JSON configuration files.
            Spec Section 4.4.3: Add to standard_tags.json or profile
        """
        if ":" not in tag:
            return False, "Tag must be in namespace:value format"

        namespace, tag_name = tag.split(":", 1)

        # Determine target file
        if profile:
            config_path = Path(f"config/profiles/{profile}.json")
        else:
            config_path = Path("config/taxonomy/standard_tags.json")

        if not config_path.exists():
            return False, f"Config file not found: {config_path}"

        try:
            # Load existing config
            with open(config_path, "r", encoding="utf-8") as f:
                config = json.load(f)

            # Prepare new tag entry
            new_tag_entry = {
                "name": tag_name,
                "description": description,
                "examples": examples,
            }

            # Add to appropriate namespace
            if profile:
                # Profile structure: additional_namespaces
                if "additional_namespaces" not in config:
                    config["additional_namespaces"] = {}

                if namespace not in config["additional_namespaces"]:
                    config["additional_namespaces"][namespace] = {"tags": []}

                config["additional_namespaces"][namespace]["tags"].append(
                    new_tag_entry
                )
            else:
                # Standard taxonomy structure: namespaces
                if "namespaces" not in config:
                    config["namespaces"] = {}

                if namespace not in config["namespaces"]:
                    config["namespaces"][namespace] = {"tags": []}

                config["namespaces"][namespace]["tags"].append(new_tag_entry)

            # Write back with pretty formatting
            with open(config_path, "w", encoding="utf-8") as f:
                json.dump(config, f, indent=2, ensure_ascii=False)
                f.write("\n")  # Add trailing newline

            self.logger.info(
                "tag_added_to_taxonomy",
                tag=tag,
                config_path=str(config_path),
                profile=profile,
            )

            return True, None

        except Exception as e:
            self.logger.error(
                "add_to_taxonomy_failed",
                tag=tag,
                config_path=str(config_path),
                error=str(e),
            )
            return False, str(e)
