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

This tool adds a SPARQL transformation step to an existing layer
with proper ordering and execution configuration.
"""

import os
import sys
import uuid
from datetime import datetime

# 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 add_transformation_step(
    layer_name_or_uri: str,
    step_name: str,
    insert_query: str,
    explanation: str,
    graphmart_uri: str = None,
    dry_run: bool = False
) -> str:
    """Create a new query step within a transformation layer.
    
    This tool adds a SPARQL transformation step to an existing layer
    with proper ordering and execution configuration.
    
    CRITICAL AGS Query Step Requirements:
    - Must use INSERT { GRAPH ${targetGraph} { ... } } pattern
    - Must include ${usingSources} in FROM/WHERE clause
    - Template variables: ${targetGraph} and ${usingSources} (single $)
    - Use \\n for line breaks (NOT \\\\n)
    - Include proper PREFIX declarations
    - Follow pattern: INSERT { GRAPH ${targetGraph} { triples } } ${usingSources} WHERE { ... }
    
    Example valid insert_query:
    ```
    PREFIX ex: <http://example.com/ontology#>
    INSERT {
        GRAPH ${targetGraph} {
            ?entity ex:hasLinkedData ?linkedEntity .
        }
    }
    ${usingSources}
    WHERE {
        ?entity a ex:SourceClass ;
            ex:linkingProperty ?linkValue .
        ?linkedEntity a ex:TargetClass ;
            ex:matchingProperty ?linkValue .
    }
    ```
    
    Args:
        layer_name_or_uri: Layer name or full URI to add step to
        step_name: Human-readable name for the step
        insert_query: The SPARQL INSERT query for the transformation
        explanation: Human-readable description of the operation (required)
        graphmart_uri: URI of the graphmart (optional, auto-detects)
        dry_run: If true, validate inputs without executing
        
    Returns:
        JSON response with query step creation results
    """
    BaseToolMixin.validate_required_param(layer_name_or_uri, "layer_name_or_uri")
    BaseToolMixin.validate_required_param(step_name, "step_name")
    BaseToolMixin.validate_required_param(insert_query, "insert_query")
    BaseToolMixin.validate_required_param(explanation, "explanation")
    
    # Validate AGS query step format requirements
    query_upper = insert_query.upper().strip()
    
    # Check for required AGS template variables
    if "${targetGraph}" not in insert_query:
        raise ToolError("insert_query must include ${targetGraph} template variable for AGS query steps")
    
    if "${usingSources}" not in insert_query:
        raise ToolError("insert_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("insert_query must start with PREFIX declarations or INSERT statement")
    
    if "INSERT" not in query_upper:
        raise ToolError("insert_query must contain INSERT statement for AGS query steps")
    
    if "GRAPH ${TARGETGRAPH}" not in query_upper:
        raise ToolError("insert_query must use 'GRAPH ${targetGraph} { }' pattern inside INSERT clause")
    
    # Warn about common escaping issues
    if "\\\\n" in insert_query:
        raise ToolError("Use single \\n for line breaks, not \\\\n in insert_query")
    
    if "$${" in insert_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 layer URI from name if needed
    layer_uri = None
    if layer_name_or_uri.startswith("http://"):
        layer_uri = layer_name_or_uri
    else:
        # Find layer by name
        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]
    
    # Generate unique URIs for the query step and ordered item
    step_uuid = str(uuid.uuid4()).replace('-', '')
    ordered_item_uuid = str(uuid.uuid4()).replace('-', '')
    
    step_uri = f"http://cambridgesemantics.com/QueryStep/{step_uuid}"
    ordered_item_uri = f"http://cambridgesemantics.com/OrderedItem/{ordered_item_uuid}"
    
    # Dry run mode
    if dry_run:
        return BaseToolMixin.create_success_response({
            "dry_run": True,
            "layer_name_or_uri": layer_name_or_uri,
            "resolved_layer_uri": layer_uri,
            "step_name": step_name,
            "step_uri": step_uri,
            "ordered_item_uri": ordered_item_uri,
            "graphmart_uri": graphmart_uri,
            "explanation": explanation
        }, "Query step creation validated. Set dry_run=false to execute.")
    
    # Get current timestamp for metadata
    current_timestamp = datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'
    
    # Get current highest step index within the target layer
    try:
        step_index_query = f"""
        PREFIX anzo: <http://openanzo.org/ontologies/2008/07/Anzo#>
        PREFIX gm: <http://cambridgesemantics.com/ontologies/Graphmarts#>
        
        SELECT ?index WHERE {{
            GRAPH <{graphmart_uri}> {{
                <{layer_uri}> gm:step ?orderedItem .
                ?orderedItem anzo:index ?index .
            }}
        }}
        """
        step_index_result = agent.anzo_client.query_journal(step_index_query)
        
        # Parse all step indices and find the maximum numerically
        next_step_index = 0
        table_results = step_index_result.as_table_results().as_list()
        
        if table_results:
            indices = []
            for result_row in table_results:
                if result_row and len(result_row) > 0:
                    index_value = result_row[0]
                    try:
                        indices.append(int(index_value))
                    except (ValueError, TypeError):
                        BaseToolMixin.log_warning(f"Skipping non-numeric step index: {index_value}")
            
            if indices:
                next_step_index = max(indices) + 1
                
    except Exception as index_error:
        BaseToolMixin.log_warning(f"Step index query failed: {index_error}")
        next_step_index = 0
    
    # Escape the SPARQL query for insertion
    escaped_query = insert_query.replace('"""', '\\"\\"\\"')
    
    # Create the query step and its metadata
    step_creation_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}> {{
            <{step_uri}> a gm:Step, gm:LayerChild, gm:TransformStep ;
                        dc:title "{step_name}" ;
                        gm:transformQuery '''{escaped_query}''' ;
                        gm:enabled "true"^^<http://www.w3.org/2001/XMLSchema#boolean> ;
                        gm:preview "false"^^<http://www.w3.org/2001/XMLSchema#boolean> ;
                        gm:disableLoadCounts "false"^^<http://www.w3.org/2001/XMLSchema#boolean> ;
                        gm:preGenerateStatistics "false"^^<http://www.w3.org/2001/XMLSchema#boolean> ;
                        gm:preVacuum "false"^^<http://www.w3.org/2001/XMLSchema#boolean> ;
                        gm:source gm:AllPrevious, gm:Self ;
                        dct:created "{current_timestamp}" ;
                        dct:creator <http://openanzo.org/system/internal/sysadmin> ;
                        dct:modified "{current_timestamp}" ;
                        dct:modifier <http://openanzo.org/system/internal/sysadmin> .
            
            <{ordered_item_uri}> a anzo:IndexedItem, anzo:OrderedValue ;
                                anzo:orderedValue <{step_uri}> ;
                                anzo:index "{next_step_index}"^^<http://www.w3.org/2001/XMLSchema#int> ;
                                dct:created "{current_timestamp}" ;
                                dct:creator <http://openanzo.org/system/internal/sysadmin> ;
                                dct:modified "{current_timestamp}" ;
                                dct:modifier <http://openanzo.org/system/internal/sysadmin> .
            
            <{layer_uri}> gm:step <{ordered_item_uri}> .
        }}
    }}
    """
    
    # Execute the query step creation
    agent.anzo_client.update_journal(step_creation_sparql)
    
    return BaseToolMixin.create_success_response({
        "step_name": step_name,
        "step_uri": step_uri,
        "ordered_item_uri": ordered_item_uri,
        "step_index": next_step_index,
        "layer_uri": layer_uri,
        "layer_name_or_uri": layer_name_or_uri,
        "graphmart_uri": graphmart_uri,
        "explanation": explanation,
        "transform_query": insert_query
    }, f"Successfully created query step '{step_name}' in layer")