"""Module for handling contradictions and redundancies in long conversations.

This module provides the LongContraRepeatOp class which manages and updates
memory entries within a conversation scope by identifying and handling
contradictions or redundancies. It extends BaseAsyncOp to provide specialized
functionality for long conversations with potential contradictory or
repetitive statements.
"""

import re
from typing import List

from flowllm.core.context import C
from flowllm.core.enumeration import Role
from flowllm.core.op import BaseAsyncOp
from flowllm.core.schema import Message
from loguru import logger

from reme_ai.schema.memory import BaseMemory, PersonalMemory


@C.register_op()
class LongContraRepeatOp(BaseAsyncOp):
    """
    Manages and updates memory entries within a conversation scope by identifying
    and handling contradictions or redundancies. It extends BaseAsyncOp to provide
    specialized functionality for long conversations with potential contradictory
    or repetitive statements.
    """

    file_path: str = __file__

    async def async_execute(self):
        """
        Analyze memories for contradictions and redundancies, resolving conflicts.

        Process:
        1. Get updated insight memories from previous operation
        2. Check for contradictions and redundancies among memories
        3. Resolve conflicts by keeping most recent/accurate information
        4. Filter out redundant memories
        5. Store cleaned memory list in context
        """
        # Get memories from previous operation
        updated_insights = self.context.response.metadata.get("updated_insight_memories", [])

        if not updated_insights:
            logger.info("No updated insight memories to process for contradictions")
            self.context.response.metadata["memory_list"] = []
            return

        # Get operation parameters
        max_memories_to_process = self.op_params.get("long_contra_repeat_max_count", 50)
        enable_processing = self.op_params.get("enable_long_contra_repeat", True)

        if not enable_processing:
            logger.info("Long contradiction/repeat processing is disabled")
            self.context.response.metadata["memory_list"] = updated_insights
            return

        # Sort memories by creation time (most recent first) and limit count
        sorted_memories = sorted(
            updated_insights,
            key=lambda x: x.time_created,
            reverse=True,
        )[:max_memories_to_process]

        if len(sorted_memories) <= 1:
            logger.info("Only one memory to process, skipping contradiction analysis")
            self.context.response.metadata["memory_list"] = sorted_memories
            return

        logger.info(f"Processing {len(sorted_memories)} memories for contradictions and redundancies")

        # Analyze and resolve contradictions
        filtered_memories = await self._analyze_and_resolve_conflicts(sorted_memories)

        # Store results in context
        self.context.response.metadata["memory_list"] = filtered_memories
        logger.info(f"Conflict resolution: {len(sorted_memories)} -> {len(filtered_memories)} memories")

    async def _analyze_and_resolve_conflicts(self, memories: List[BaseMemory]) -> List[BaseMemory]:
        """
        Analyze memories for contradictions and redundancies using LLM.

        Args:
            memories: List of memories to analyze

        Returns:
            List of filtered memories with conflicts resolved
        """
        user_name = self.context.get("user_name", "user")

        # Prepare memory content for LLM analysis
        memory_texts = []
        for i, memory in enumerate(memories):
            memory_texts.append(f"{i + 1} {memory.content}")

        # Build LLM prompt
        system_prompt = self.prompt_format(
            prompt_name="long_contra_repeat_system",
            num_obs=len(memory_texts),
            user_name=user_name,
        )
        few_shot = self.prompt_format(
            prompt_name="long_contra_repeat_few_shot",
            user_name=user_name,
        )
        user_query = self.prompt_format(
            prompt_name="long_contra_repeat_user_query",
            user_query="\n".join(memory_texts),
        )

        full_prompt = f"{system_prompt}\n\n{few_shot}\n\n{user_query}"
        logger.info(f"Contradiction analysis prompt length: {len(full_prompt)} chars")

        # Get LLM analysis
        response = await self.llm.achat([Message(role=Role.USER, content=full_prompt)])

        if not response or not response.content:
            logger.warning("Empty response from LLM, keeping all memories")
            return memories

        # Parse response and filter memories
        return self._parse_and_filter_memories(response.content, memories, user_name)

    @staticmethod
    def _parse_and_filter_memories(response_text: str, memories: List[BaseMemory], user_name: str) -> List[BaseMemory]:
        """Parse LLM response and filter memories based on contradiction/containment analysis"""

        # Use class method to parse the response
        judgments = LongContraRepeatOp.parse_long_contra_repeat_response(response_text)

        if not judgments:
            logger.warning("No valid judgments found in response")
            return memories

        # Process each judgment
        filtered_memories = []
        processed_indices = set()

        for idx, judgment, modified_content in judgments:
            try:
                memory_idx = idx - 1  # Convert to 0-based index
                if memory_idx >= len(memories):
                    logger.warning(f"Invalid index {memory_idx} for memories list of length {len(memories)}")
                    continue

                processed_indices.add(memory_idx)
                memory = memories[memory_idx]
                judgment_lower = judgment.lower()

                if judgment_lower in ["矛盾", "contradiction"]:
                    # For contradictory memories, either modify content or mark for removal
                    if modified_content.strip():
                        # Create new memory with modified content
                        modified_memory = PersonalMemory(
                            workspace_id=memory.workspace_id,
                            memory_id=memory.memory_id,
                            content=modified_content.strip(),
                            target=memory.target if hasattr(memory, "target") else user_name,
                            author=memory.author,
                            metadata={**memory.metadata, "modified_by": "long_contra_repeat"},
                        )
                        modified_memory.update_time_modified()
                        filtered_memories.append(modified_memory)
                        logger.info(f"Modified contradictory memory {idx}: {modified_content.strip()[:50]}...")
                    else:
                        # Remove contradictory memory without modification
                        logger.info(f"Removing contradictory memory {idx}: {memory.content[:50]}...")

                elif judgment_lower in ["被包含", "contained"]:
                    # Remove contained/redundant memories
                    logger.info(f"Removing contained memory {idx}: {memory.content[:50]}...")

                else:  # 'none' case
                    # Keep the memory as is
                    filtered_memories.append(memory)

            except Exception as e:
                logger.warning(f"Error processing judgment for index {idx}: {e}")
                continue

        # Add any memories that weren't processed (shouldn't happen with correct LLM response)
        for i, memory in enumerate(memories):
            if i not in processed_indices:
                filtered_memories.append(memory)
                logger.warning(f"Memory {i + 1} was not processed by LLM, keeping as is")

        return filtered_memories

    def get_language_value(self, value_dict: dict):
        """Get language-specific value from dictionary.

        Args:
            value_dict: Dictionary mapping language codes to values

        Returns:
            The value corresponding to the current language, or the English
            value as fallback if the current language is not found
        """
        return value_dict.get(self.language, value_dict.get("en"))

    @staticmethod
    def parse_long_contra_repeat_response(response_text: str) -> List[tuple]:
        """Parse long contra repeat response to extract judgments"""
        # Pattern to match both Chinese and English judgment formats
        # Chinese: 判断：<序号> <矛盾|被包含|无> <修改后的内容>
        # English: Judgment: <Index> <Contradiction|Contained|None> <Modified content>
        pattern = (
            r"判断：<(\d+)>\s*<(矛盾|被包含|无)>\s*<([^<>]*)>|"
            r"Judgment:\s*<(\d+)>\s*<(Contradiction|Contained|None)>\s*<([^<>]*)>"
        )
        matches = re.findall(pattern, response_text, re.IGNORECASE | re.MULTILINE)

        judgments = []
        for match in matches:
            # Handle both Chinese and English patterns
            if match[0]:  # Chinese pattern
                idx_str, judgment, modified_content = match[0], match[1], match[2]
            else:  # English pattern
                idx_str, judgment, modified_content = match[3], match[4], match[5]

            try:
                idx = int(idx_str)
                judgments.append((idx, judgment, modified_content))
            except ValueError:
                logger.warning(f"Invalid index format: {idx_str}")
                continue

        logger.info(f"Parsed {len(judgments)} long contra repeat judgments from response")
        return judgments
