"""
REST API Utilities for AGS

This module provides centralized utilities for making REST API calls to AGS,
including authentication, connection configuration, and common operations.
"""

import os
import json
import base64
import time
import urllib.request
import urllib.parse
import urllib.error
from typing import Dict, Any, Optional, Tuple


def get_ags_env_config() -> Dict[str, str]:
    """
    Load AGS connection configuration from MCP config or environment variables.
    
    Uses the same pattern as existing PyAnzo tools - checks MCP config first,
    then falls back to environment variables.
    
    Returns:
        Dictionary with connection configuration
        
    Raises:
        ValueError: If required configuration is missing
    """
    config = {
        'ANZO_SERVER': os.getenv('ANZO_SERVER'),
        'ANZO_PORT': os.getenv('ANZO_PORT', '443'),
        'ANZO_USERNAME': os.getenv('ANZO_USERNAME'),
        'ANZO_PASSWORD': os.getenv('ANZO_PASSWORD'),
        'GRAPHMART_URI': os.getenv('GRAPHMART_URI')
    }
    
    # Validate required configuration
    missing_vars = [var for var, value in config.items() if not value]
    if missing_vars:
        raise ValueError(f"Missing required AGS configuration: {missing_vars}. "
                        f"Set environment variables or use --config flag.")
    
    return config


def create_auth_header(username: str, password: str) -> str:
    """Create HTTP Basic Authentication header."""
    credentials = f"{username}:{password}"
    encoded_credentials = base64.b64encode(credentials.encode('utf-8')).decode('ascii')
    return f"Basic {encoded_credentials}"


def extract_graphmart_id(graphmart_uri: str) -> str:
    """Extract and URL-encode the graphmart ID from the full URI for API calls."""
    return urllib.parse.quote(graphmart_uri, safe='')


def make_ags_request(
    config: Dict[str, str], 
    endpoint: str, 
    method: str = 'GET',
    data: Optional[bytes] = None,
    timeout: int = 30
) -> Tuple[int, Dict[str, Any]]:
    """
    Make an authenticated request to the AGS REST API.
    
    Args:
        config: AGS connection configuration
        endpoint: API endpoint (e.g., '/graphmarts/{id}/status')
        method: HTTP method ('GET', 'POST', etc.)
        data: Request body data (bytes)
        timeout: Request timeout in seconds
        
    Returns:
        Tuple of (status_code, response_json)
        
    Raises:
        urllib.error.HTTPError: For HTTP errors
        urllib.error.URLError: For connection errors
        json.JSONDecodeError: For invalid JSON responses
    """
    server = config['ANZO_SERVER']
    port = config['ANZO_PORT']
    username = config['ANZO_USERNAME']
    password = config['ANZO_PASSWORD']
    
    # Construct full URL
    protocol = 'https' if port == '443' else 'http'
    base_url = f"{protocol}://{server}:{port}/api"
    
    # Ensure endpoint starts with /
    if not endpoint.startswith('/'):
        endpoint = '/' + endpoint
    
    full_url = f"{base_url}{endpoint}"
    
    # Create request
    request = urllib.request.Request(full_url, data=data, method=method)
    
    # Add authentication header
    auth_header = create_auth_header(username, password)
    request.add_header('Authorization', auth_header)
    
    # Add content type for POST requests
    if method in ['POST', 'PUT', 'PATCH']:
        request.add_header('Content-Type', 'application/json')
    
    # Make the request
    with urllib.request.urlopen(request, timeout=timeout) as response:
        status_code = response.getcode()
        response_body = response.read().decode('utf-8')
        
        # Parse JSON response
        if response_body:
            try:
                response_json = json.loads(response_body)
            except json.JSONDecodeError:
                # If not valid JSON, wrap in a message object
                response_json = {"message": response_body}
        else:
            response_json = {}
    
    return status_code, response_json


def wait_for_graphmart_status(
    config: Dict[str, str],
    target_status: str,
    poll_interval: int = 3,
    max_wait_time: int = 300
) -> Tuple[bool, str, Dict[str, Any]]:
    """
    Poll the graphmart status until it reaches the target status.
    
    Args:
        config: AGS connection configuration
        target_status: Target status URI to wait for
        poll_interval: Seconds between status checks
        max_wait_time: Maximum time to wait in seconds
        
    Returns:
        Tuple of (success, final_status, final_response)
    """
    graphmart_uri = config['GRAPHMART_URI']
    graphmart_id = extract_graphmart_id(graphmart_uri)
    endpoint = f"/graphmarts/{graphmart_id}/status"
    
    start_time = time.time()
    attempt = 0
    
    while True:
        attempt += 1
        elapsed_time = int(time.time() - start_time)
        
        # Check timeout
        if elapsed_time >= max_wait_time:
            try:
                # Get final status for return
                _, final_response = make_ags_request(config, endpoint)
                final_status = final_response.get('status', 'Unknown')
                return False, final_status, final_response
            except Exception:
                return False, 'Unknown', {}
        
        try:
            # Check current status
            status_code, response = make_ags_request(config, endpoint)
            current_status = response.get('status', 'Unknown')
            
            # Check if we've reached the target status
            if current_status == target_status:
                return True, current_status, response
            
            # Wait before next check (unless this is already the target)
            if current_status != target_status:
                time.sleep(poll_interval)
                
        except Exception as e:
            # On error, wait and retry
            time.sleep(poll_interval)
            continue


def get_graphmart_status(config: Dict[str, str], detailed: bool = True) -> Tuple[int, Dict[str, Any]]:
    """
    Get the current status of the graphmart.
    
    Args:
        config: AGS connection configuration
        detailed: If True, include detailed status information
        
    Returns:
        Tuple of (status_code, response_json)
    """
    graphmart_uri = config['GRAPHMART_URI']
    graphmart_id = extract_graphmart_id(graphmart_uri)
    
    endpoint = f"/graphmarts/{graphmart_id}/status"
    if detailed:
        endpoint += "?detail=true"
    
    return make_ags_request(config, endpoint)