"""
Update transformation step tool for the AGS SPARQL Agent.
"""

import os
import sys

# Add src directory to path for imports
src_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
if src_dir not in sys.path:
    sys.path.insert(0, src_dir)

from ..base_tool import BaseToolMixin, handle_tool_errors, ToolError
from ...utils.agent_utils import get_agent, get_graphmart_uri


@handle_tool_errors
async def update_transformation_step(
    step_name_or_uri: str,
    new_title: str = None,
    new_transform_query: str = None,
    new_order_index: int = None,
    new_enabled: bool = None,
    layer_name_or_uri: str = None,
    graphmart_uri: str = None,
    explanation: str = None,
    dry_run: bool = False
) -> str:
    """Update properties of an existing transformation step.
    
    This tool allows updating step properties without deleting and recreating
    the step, preserving relationships and avoiding disruption.
    
    Args:
        step_name_or_uri: Step name or full URI to update
        new_title: New title for the step (optional)
        new_transform_query: New SPARQL query for the step (optional)
        new_order_index: New order index for the step within its layer (optional)
        new_enabled: New enabled status for the step (optional)
        layer_name_or_uri: Layer name or URI (optional, helps with lookup)
        graphmart_uri: URI of the graphmart (optional, auto-detects)
        explanation: Human-readable description of the operation
        dry_run: If true, validate inputs without executing
        
    Returns:
        JSON response with update results
    """
    from datetime import datetime
    
    BaseToolMixin.validate_required_param(step_name_or_uri, "step_name_or_uri")
    
    # Validate that at least one update parameter is provided
    update_params = [new_title, new_transform_query, new_order_index, new_enabled]
    if all(param is None for param in update_params):
        raise ToolError("At least one update parameter (new_title, new_transform_query, new_order_index, new_enabled) must be provided")
    
    # Validate new_transform_query if provided (same as add_transformation_step)
    if new_transform_query is not None:
        query_upper = new_transform_query.upper().strip()
        
        # Check for required AGS template variables
        if "${targetGraph}" not in new_transform_query:
            raise ToolError("new_transform_query must include ${targetGraph} template variable for AGS query steps")
        
        if "${usingSources}" not in new_transform_query:
            raise ToolError("new_transform_query must include ${usingSources} template variable for AGS query steps")
        
        # Check for correct INSERT...GRAPH pattern
        if not query_upper.startswith("PREFIX") and not query_upper.startswith("INSERT"):
            raise ToolError("new_transform_query must start with PREFIX declarations or INSERT statement")
        
        if "INSERT" not in query_upper:
            raise ToolError("new_transform_query must contain INSERT statement for AGS query steps")
        
        if "GRAPH ${TARGETGRAPH}" not in query_upper:
            raise ToolError("new_transform_query must use 'GRAPH ${targetGraph} { }' pattern inside INSERT clause")
        
        # Warn about common escaping issues
        if "\\\\n" in new_transform_query:
            raise ToolError("Use single \\n for line breaks, not \\\\n in new_transform_query")
        
        if "$${" in new_transform_query:
            raise ToolError("Use single $ for template variables: ${targetGraph} and ${usingSources}, not $${}")
    
    # Auto-detect graphmart URI if not provided
    graphmart_uri = get_graphmart_uri(graphmart_uri)
    
    agent = await get_agent()
    
    # Resolve step URI and get current values
    step_uri = None
    step_ordered_item_uri = None
    parent_layer_uri = None
    current_title = None
    current_transform_query = None
    current_index = None
    current_enabled = None
    
    if step_name_or_uri.startswith("http://"):
        step_uri = step_name_or_uri
        # Find the ordered item and parent layer for this step
        step_info_query = f"""
        PREFIX gm: <http://cambridgesemantics.com/ontologies/Graphmarts#>
        PREFIX anzo: <http://openanzo.org/ontologies/2008/07/Anzo#>
        PREFIX dc: <http://purl.org/dc/elements/1.1/>
        
        SELECT ?orderedItem ?layer ?index ?title ?transformQuery ?enabled WHERE {{
            GRAPH <{graphmart_uri}> {{
                ?layer gm:step ?orderedItem .
                ?orderedItem anzo:orderedValue <{step_uri}> ;
                            anzo:index ?index .
                <{step_uri}> dc:title ?title .
                OPTIONAL {{ <{step_uri}> gm:transformQuery ?transformQuery }}
                OPTIONAL {{ <{step_uri}> gm:enabled ?enabled }}
            }}
        }}
        """
    else:
        # Find step by name, optionally within a specific layer
        if layer_name_or_uri:
            # Resolve layer URI first if provided
            layer_uri = None
            if layer_name_or_uri.startswith("http://"):
                layer_uri = layer_name_or_uri
            else:
                layer_lookup_query = f"""
                PREFIX gm: <http://cambridgesemantics.com/ontologies/Graphmarts#>
                PREFIX anzo: <http://openanzo.org/ontologies/2008/07/Anzo#>
                PREFIX dc: <http://purl.org/dc/elements/1.1/>
                
                SELECT ?layer WHERE {{
                    GRAPH <{graphmart_uri}> {{
                        <{graphmart_uri}> gm:layer ?orderedItem .
                        ?orderedItem anzo:orderedValue ?layer .
                        ?layer dc:title "{layer_name_or_uri}" .
                    }}
                }}
                """
                lookup_result = agent.anzo_client.query_journal(layer_lookup_query)
                table_results = lookup_result.as_table_results().as_list()
                
                if not table_results or len(table_results) == 0:
                    raise ToolError(f"Layer '{layer_name_or_uri}' not found in graphmart")
                
                layer_uri = table_results[0][0]
            
            # Find step by name within the specific layer
            step_info_query = f"""
            PREFIX gm: <http://cambridgesemantics.com/ontologies/Graphmarts#>
            PREFIX anzo: <http://openanzo.org/ontologies/2008/07/Anzo#>
            PREFIX dc: <http://purl.org/dc/elements/1.1/>
            
            SELECT ?step ?orderedItem ?layer ?index ?transformQuery ?enabled WHERE {{
                GRAPH <{graphmart_uri}> {{
                    <{layer_uri}> gm:step ?orderedItem .
                    ?orderedItem anzo:orderedValue ?step ;
                                anzo:index ?index .
                    ?step dc:title "{step_name_or_uri}" .
                    OPTIONAL {{ ?step gm:transformQuery ?transformQuery }}
                    OPTIONAL {{ ?step gm:enabled ?enabled }}
                    BIND(<{layer_uri}> AS ?layer)
                }}
            }}
            """
        else:
            # Find step by name across all layers
            step_info_query = f"""
            PREFIX gm: <http://cambridgesemantics.com/ontologies/Graphmarts#>
            PREFIX anzo: <http://openanzo.org/ontologies/2008/07/Anzo#>
            PREFIX dc: <http://purl.org/dc/elements/1.1/>
            
            SELECT ?step ?orderedItem ?layer ?index ?transformQuery ?enabled WHERE {{
                GRAPH <{graphmart_uri}> {{
                    ?layer gm:step ?orderedItem .
                    ?orderedItem anzo:orderedValue ?step ;
                                anzo:index ?index .
                    ?step dc:title "{step_name_or_uri}" .
                    OPTIONAL {{ ?step gm:transformQuery ?transformQuery }}
                    OPTIONAL {{ ?step gm:enabled ?enabled }}
                }}
            }}
            """
    
    info_result = agent.anzo_client.query_journal(step_info_query)
    info_table = info_result.as_table_results().as_list()
    
    if not info_table or len(info_table) == 0:
        raise ToolError(f"Step '{step_name_or_uri}' not found in graphmart")
    
    if len(info_table) > 1:
        layer_names = []
        for result in info_table:
            layer_names.append(result[2] if len(result) > 2 else "Unknown")
        raise ToolError(f"Multiple steps named '{step_name_or_uri}' found. Please specify layer_name_or_uri.")
    
    if step_uri is None:
        step_uri = info_table[0][0]
        step_ordered_item_uri = info_table[0][1]
        parent_layer_uri = info_table[0][2]
        current_index = info_table[0][3]
        current_title = step_name_or_uri  # We know the title since we searched by it
        current_transform_query = info_table[0][4] if len(info_table[0]) > 4 else None
        current_enabled = info_table[0][5] if len(info_table[0]) > 5 else None
    else:
        step_ordered_item_uri = info_table[0][0]
        parent_layer_uri = info_table[0][1]
        current_index = info_table[0][2]
        current_title = step_name_or_uri  # We know the title since we searched by it
        current_transform_query = info_table[0][3] if len(info_table[0]) > 3 else None
        current_enabled = info_table[0][4] if len(info_table[0]) > 4 else None
    
    # Check if new_order_index conflicts with existing steps in the same layer
    if new_order_index is not None and str(new_order_index) != str(current_index):
        # Check for conflicts with other steps in the same layer
        index_check_query = f"""
        PREFIX anzo: <http://openanzo.org/ontologies/2008/07/Anzo#>
        PREFIX gm: <http://cambridgesemantics.com/ontologies/Graphmarts#>
        PREFIX dc: <http://purl.org/dc/elements/1.1/>
        
        SELECT ?existingStep ?existingTitle WHERE {{
            GRAPH <{graphmart_uri}> {{
                <{parent_layer_uri}> gm:step ?existingOrderedItem .
                ?existingOrderedItem anzo:orderedValue ?existingStep ;
                                   anzo:index "{new_order_index}"^^<http://www.w3.org/2001/XMLSchema#int> .
                ?existingStep dc:title ?existingTitle .
                FILTER(?existingStep != <{step_uri}>)
            }}
        }}
        """
        conflict_result = agent.anzo_client.query_journal(index_check_query)
        conflict_table = conflict_result.as_table_results().as_list()
        
        if conflict_table and len(conflict_table) > 0:
            conflicting_step = conflict_table[0][1] if len(conflict_table[0]) > 1 else "Unknown"
            raise ToolError(f"Order index {new_order_index} is already used by step '{conflicting_step}' in the same layer. Choose a different index.")
    
    # Build update operations
    updates = []
    update_operations = []
    current_timestamp = datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'
    
    if new_title and new_title != current_title:
        updates.append(f"title: '{current_title}' → '{new_title}'")
        update_operations.append({
            "delete": f'<{step_uri}> dc:title ?oldTitle .',
            "delete_where": f'<{step_uri}> dc:title ?oldTitle .',
            "insert": f'<{step_uri}> dc:title "{new_title}" .'
        })
    
    if new_transform_query is not None and new_transform_query != current_transform_query:
        escaped_new_query = new_transform_query.replace('"""', '\\"\\"\\"')
        updates.append(f"transform query: updated")
        update_operations.append({
            "delete": f"<{step_uri}> gm:transformQuery ?oldQuery .",
            "delete_where": f"<{step_uri}> gm:transformQuery ?oldQuery .",
            "insert": f"<{step_uri}> gm:transformQuery '''{escaped_new_query}''' ."
        })
    
    if new_order_index is not None and str(new_order_index) != str(current_index):
        updates.append(f"order index: {current_index} → {new_order_index}")
        update_operations.append({
            "delete": f'<{step_ordered_item_uri}> anzo:index ?oldIndex .',
            "delete_where": f'<{step_ordered_item_uri}> anzo:index ?oldIndex .',
            "insert": f'<{step_ordered_item_uri}> anzo:index "{new_order_index}"^^<http://www.w3.org/2001/XMLSchema#int> .'
        })
    
    if new_enabled is not None and str(new_enabled).lower() != str(current_enabled).lower():
        enabled_value = "true" if new_enabled else "false"
        updates.append(f"enabled: {current_enabled} → {enabled_value}")
        update_operations.append({
            "delete": f'<{step_uri}> gm:enabled ?oldEnabled .',
            "delete_where": f'<{step_uri}> gm:enabled ?oldEnabled .',
            "insert": f'<{step_uri}> gm:enabled "{enabled_value}"^^<http://www.w3.org/2001/XMLSchema#boolean> .'
        })
    
    # Always update the modified timestamp if changes are made
    if updates:
        update_operations.append({
            "delete": f'<{step_uri}> dct:modified ?oldTimestamp .',
            "delete_where": f'<{step_uri}> dct:modified ?oldTimestamp .',
            "insert": f'<{step_uri}> dct:modified "{current_timestamp}" .'
        })
    
    if not updates:
        return BaseToolMixin.create_success_response({
            "step_name_or_uri": step_name_or_uri,
            "resolved_step_uri": step_uri,
            "parent_layer_uri": parent_layer_uri,
            "graphmart_uri": graphmart_uri,
            "changes": "No changes needed"
        }, "No updates required - all values are already current")
    
    # Dry run mode
    if dry_run:
        return BaseToolMixin.create_success_response({
            "dry_run": True,
            "step_name_or_uri": step_name_or_uri,
            "resolved_step_uri": step_uri,
            "step_ordered_item_uri": step_ordered_item_uri,
            "parent_layer_uri": parent_layer_uri,
            "proposed_updates": updates,
            "current_values": {
                "title": current_title,
                "transform_query": current_transform_query,
                "order_index": current_index,
                "enabled": current_enabled
            },
            "new_values": {
                "title": new_title or current_title,
                "transform_query": new_transform_query if new_transform_query is not None else current_transform_query,
                "order_index": new_order_index if new_order_index is not None else current_index,
                "enabled": new_enabled if new_enabled is not None else current_enabled
            },
            "graphmart_uri": graphmart_uri,
            "explanation": explanation
        }, f"Step update validated. Would make {len(updates)} changes. Set dry_run=false to execute.")
    
    # Execute the updates
    for operation in update_operations:
        if operation.get("delete") and operation.get("delete_where"):
            delete_sparql = f"""
            PREFIX gm: <http://cambridgesemantics.com/ontologies/Graphmarts#>
            PREFIX dc: <http://purl.org/dc/elements/1.1/>
            PREFIX dct: <http://purl.org/dc/terms/>
            PREFIX anzo: <http://openanzo.org/ontologies/2008/07/Anzo#>
            
            DELETE {{
                GRAPH <{graphmart_uri}> {{
                    {operation["delete"]}
                }}
            }}
            WHERE {{
                GRAPH <{graphmart_uri}> {{
                    {operation["delete_where"]}
                }}
            }}
            """
            agent.anzo_client.update_journal(delete_sparql)
        
        if operation["insert"]:
            insert_sparql = f"""
            PREFIX gm: <http://cambridgesemantics.com/ontologies/Graphmarts#>
            PREFIX dc: <http://purl.org/dc/elements/1.1/>
            PREFIX dct: <http://purl.org/dc/terms/>
            PREFIX anzo: <http://openanzo.org/ontologies/2008/07/Anzo#>
            
            INSERT DATA {{
                GRAPH <{graphmart_uri}> {{
                    {operation["insert"]}
                }}
            }}
            """
            agent.anzo_client.update_journal(insert_sparql)
    
    return BaseToolMixin.create_success_response({
        "step_name_or_uri": step_name_or_uri,
        "updated_step_uri": step_uri,
        "step_ordered_item_uri": step_ordered_item_uri,
        "parent_layer_uri": parent_layer_uri,
        "applied_updates": updates,
        "updated_values": {
            "title": new_title or current_title,
            "transform_query": new_transform_query if new_transform_query is not None else current_transform_query,
            "order_index": new_order_index if new_order_index is not None else current_index,
            "enabled": new_enabled if new_enabled is not None else current_enabled
        },
        "graphmart_uri": graphmart_uri,
        "explanation": explanation
    }, f"Successfully updated transformation step '{step_name_or_uri}' with {len(updates)} changes")