import threading
from typing import Set
from analogai.foundation.common.dtos.notion_expression import NotionExpression
from analogai.foundation.core.value_objects import Relation
from analogai.foundation.modules.notion.notion_service import NotionService
from analogai.deepthink.application.value_objects.user_agent import UserAgent
from analogai.foundation.common.logging.app_logger import app_logger
from analogai.deepthink.features.notion_enrichment.models import EnrichmentSuggestion
from analogai.deepthink.features.notion_enrichment.ports import NotionEnrichmentGatewayPort

MAX_ENRICHMENT_STEPS = 3


class NotionEnricherService:
    def __init__(
        self,
        statement_service,
        notion_service: NotionService,
        gateway: NotionEnrichmentGatewayPort,
    ):
        self._statement_service = statement_service
        self._notion_service = notion_service
        self._gateway = gateway
        self._active_captions: Set[str] = set()
        self._active_lock = threading.Lock()

    def handle_notion_created(self, notion: object) -> None:
        if not notion:
            return
        caption = (notion.caption or "").strip()
        if not caption:
            return
        if not self._acquire_caption(caption):
            return
        app_logger.info("Starting notion enrichment: caption='%s'", caption)
        user_id = notion.user_id or ""
        agent_id = notion.agent_id or ""
        user_agent = UserAgent(
            user_id=user_id,
            agent_id=agent_id,
            user_authority=1.0,
        )
        try:
            enriched: Set[str] = set()
            queue: Set[str] = {caption}
            for step in range(1, MAX_ENRICHMENT_STEPS + 1):
                app_logger.info(
                    "Enrichment step %d/%d: queue_size=%d, enriched_total=%d",
                    step,
                    MAX_ENRICHMENT_STEPS,
                    len(queue),
                    len(enriched),
                )
                next_queue: Set[str] = set()
                for caption in queue:
                    if caption in enriched:
                        continue
                    enriched.add(caption)
                    try:
                        result = self._gateway.enrich(caption, user_agent)
                    except Exception as exc:
                        app_logger.warning(f"Enrichment gateway failed for '{caption}': {exc}")
                        continue
                    if not result.suggestions:
                        app_logger.info("No enrichment suggestions for caption='%s'", caption)
                        continue
                    app_logger.info(
                        "Enrichment suggestions received: caption='%s', count=%d",
                        caption,
                        len(result.suggestions),
                    )
                    for suggestion in result.suggestions:
                        app_logger.info(
                            "Submitting enrichment statement: subject='%s', relationship='%s', complement='%s'",
                            suggestion.subject_caption,
                            suggestion.relationship_type.value,
                            suggestion.complement_caption,
                        )
                        self._submit_learner_request(suggestion, user_agent)
                        next_queue.add(suggestion.subject_caption)
                        next_queue.add(suggestion.complement_caption)
                queue = next_queue - enriched
                if not queue:
                    break
        finally:
            self._release_caption(caption)
            app_logger.info("Finished notion enrichment: initial_caption='%s'", caption)

    def _submit_learner_request(self, suggestion: EnrichmentSuggestion, user_agent: UserAgent) -> None:
        try:
            subject_expression = NotionExpression(caption=suggestion.subject_caption)
            complement_expression = NotionExpression(caption=suggestion.complement_caption)
            subject_notion = self._notion_service.find_or_create(
                user_agent.user_id,
                user_agent.agent_id,
                subject_expression,
            )
            complement_notion = self._notion_service.find_or_create(
                user_agent.user_id,
                user_agent.agent_id,
                complement_expression,
            )
            if self._is_notions_same(subject_notion, complement_notion):
                app_logger.info(
                    "Skipping enrichment statement with identical notions: subject='%s', complement='%s'",
                    suggestion.subject_caption,
                    suggestion.complement_caption,
                )
                return
            relation = Relation(
                value=suggestion.relationship_type.value,
                _relationship_type=suggestion.relationship_type,
            )
            self._statement_service.create(
                user_id=user_agent.user_id,
                agent_id=user_agent.agent_id,
                subject_notion=subject_notion,
                relation=relation,
                complement_notion=complement_notion,
                probability=1.0,
                validity=self._get_validity(user_agent),
                modifier=None,
            )
        except Exception as exc:
            statement_fmt = (
                f"[subject:{suggestion.subject_caption}]"
                f"[relationship:{suggestion.relationship_type.value}][complement:{suggestion.complement_caption}]"
            )
            app_logger.warning(f"Failed to submit enrichment statement '{statement_fmt}': {exc}")

    def _get_validity(self, user_agent: UserAgent) -> float:
        return max(user_agent.user_authority, 0.1)

    def _is_notions_same(self, subject_notion, complement_notion) -> bool:
        if subject_notion is None or complement_notion is None:
            return False
        return subject_notion.id == complement_notion.id

    def _acquire_caption(self, caption: str) -> bool:
        normalized = caption.strip().lower()
        with self._active_lock:
            if normalized in self._active_captions:
                return False
            self._active_captions.add(normalized)
            return True

    def _release_caption(self, caption: str) -> None:
        normalized = caption.strip().lower()
        with self._active_lock:
            self._active_captions.discard(normalized)

