"""
Update direct load step tool for the AGS SPARQL Agent.

Uses REST API (identical in v5.4 and v6.2) instead of journal writes.
"""

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, get_rest_api_client


@handle_tool_errors
async def update_direct_load_step(
    step_name_or_uri: str,
    new_title: str = None,
    new_file_path: str = None,
    new_file_pattern: str = None,
    new_model_name: str = None,
    new_ontology_namespace: str = None,
    new_base_uri: 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 direct load step.
    
    This tool allows updating direct load 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_file_path: New directory path containing the JSON files (optional)
        new_file_pattern: New file pattern to match (optional)
        new_model_name: New model name for the data (optional)
        new_ontology_namespace: New ontology namespace URI (optional)
        new_base_uri: New base URI for generated entities (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
    """
    import re
    
    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_file_path, new_file_pattern, new_model_name, 
                    new_ontology_namespace, new_base_uri, new_order_index, new_enabled]
    if all(param is None for param in update_params):
        raise ToolError("At least one update parameter must be provided")
    
    # 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 direct load 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"Direct load step '{step_name_or_uri}' not found in graphmart")
    
    if len(info_table) > 1:
        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
        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
        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
    
    # Parse current transform query to extract current values
    current_file_path = None
    current_file_pattern = None
    current_model_name = None
    current_ontology_namespace = None
    current_base_uri = None
    
    if current_transform_query:
        # Extract current values from the DataToolkit transform query
        file_path_match = re.search(r's:url "([^"]*)"', current_transform_query)
        if file_path_match:
            current_file_path = file_path_match.group(1)
        
        file_pattern_match = re.search(r's:pattern "([^"]*)"', current_transform_query)
        if file_pattern_match:
            current_file_pattern = file_pattern_match.group(1)
        
        model_name_match = re.search(r's:model "([^"]*)"', current_transform_query)
        if model_name_match:
            current_model_name = model_name_match.group(1)
        
        ontology_match = re.search(r's:ontology <([^>]*)>', current_transform_query)
        if ontology_match:
            current_ontology_namespace = ontology_match.group(1)
        
        base_match = re.search(r's:base <([^>]*)>', current_transform_query)
        if base_match:
            current_base_uri = base_match.group(1)
    
    # 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):
        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 list of changes for reporting and construct the REST API payload
    updates = []
    patch_kwargs = {}
    new_transform_query = None
    
    # Track which DataToolkit parameters need updating
    new_dt_file_path = new_file_path or current_file_path
    new_dt_file_pattern = new_file_pattern or current_file_pattern
    new_dt_model_name = new_model_name or current_model_name
    new_dt_ontology_namespace = new_ontology_namespace or current_ontology_namespace
    new_dt_base_uri = new_base_uri or current_base_uri
    
    if new_title and new_title != current_title:
        updates.append(f"title: '{current_title}' → '{new_title}'")
        patch_kwargs["title"] = new_title
    
    # Check if any DataToolkit parameters changed — need to rebuild the transform query
    if (new_file_path and new_file_path != current_file_path) or \
       (new_file_pattern and new_file_pattern != current_file_pattern) or \
       (new_model_name and new_model_name != current_model_name) or \
       (new_ontology_namespace and new_ontology_namespace != current_ontology_namespace) or \
       (new_base_uri and new_base_uri != current_base_uri):
        
        # Build the updated DataToolkit query
        new_transform_query = f'''PREFIX s:  <http://cambridgesemantics.com/ontologies/DataToolkit#>
PREFIX rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX xsd:  <http://www.w3.org/2001/XMLSchema#>
PREFIX owl:  <http://www.w3.org/2002/07/owl#>
PREFIX anzo: <http://openanzo.org/ontologies/2008/07/Anzo>
PREFIX zowl: <http://openanzo.org/ontologies/2009/05/AnzoOwl#>
PREFIX dc:  <http://purl.org/dc/elements/1.1/>

INSERT
{{
 GRAPH ${{targetGraph}}
 {{
  ?subject ?predicate ?object .
 }}
}}
WHERE
{{
 SERVICE <http://cambridgesemantics.com/services/DataToolkit>
 {{
  ?data a s:FileSource ;
   s:url "{new_dt_file_path}" ;
   s:pattern "{new_dt_file_pattern}" ;
   s:model "{new_dt_model_name}" ;
   .

  ?generator a s:OntologyGenerator , s:RdfGenerator ;
   s:concurrency 1 ;
   s:as (?subject ?predicate ?object ?graph) ;
   s:ontology <{new_dt_ontology_namespace}> ;
   s:base <{new_dt_base_uri}> ;
   .
 }}
}}'''
        
        if new_file_path and new_file_path != current_file_path:
            updates.append(f"file_path: '{current_file_path}' → '{new_file_path}'")
        if new_file_pattern and new_file_pattern != current_file_pattern:
            updates.append(f"file_pattern: '{current_file_pattern}' → '{new_file_pattern}'")
        if new_model_name and new_model_name != current_model_name:
            updates.append(f"model_name: '{current_model_name}' → '{new_model_name}'")
        if new_ontology_namespace and new_ontology_namespace != current_ontology_namespace:
            updates.append(f"ontology_namespace: '{current_ontology_namespace}' → '{new_ontology_namespace}'")
        if new_base_uri and new_base_uri != current_base_uri:
            updates.append(f"base_uri: '{current_base_uri}' → '{new_base_uri}'")
        
        patch_kwargs["transformQuery"] = new_transform_query
    
    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}")
        patch_kwargs["enabled"] = new_enabled
    
    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}")
    
    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,
                "file_path": current_file_path,
                "file_pattern": current_file_pattern,
                "model_name": current_model_name,
                "ontology_namespace": current_ontology_namespace,
                "base_uri": current_base_uri,
                "order_index": current_index,
                "enabled": current_enabled
            },
            "new_values": {
                "title": new_title or current_title,
                "file_path": new_file_path or current_file_path,
                "file_pattern": new_file_pattern or current_file_pattern,
                "model_name": new_model_name or current_model_name,
                "ontology_namespace": new_ontology_namespace or current_ontology_namespace,
                "base_uri": new_base_uri or current_base_uri,
                "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"Direct load step update validated. Would make {len(updates)} changes. Set dry_run=false to execute.")
    
    # Execute updates via REST API (single PATCH replaces multi-step DELETE/INSERT)
    api = get_rest_api_client()
    
    if patch_kwargs:
        api.modify_step(step_uri, step_type="DirectLoadStep", **patch_kwargs)
    
    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,
            "file_path": new_file_path or current_file_path,
            "file_pattern": new_file_pattern or current_file_pattern,
            "model_name": new_model_name or current_model_name,
            "ontology_namespace": new_ontology_namespace or current_ontology_namespace,
            "base_uri": new_base_uri or current_base_uri,
            "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 direct load step '{step_name_or_uri}' with {len(updates)} changes")