"""
API call detection - find API calls in frontend/client code using AST parsing
"""
import re
from pathlib import Path
from typing import List, Dict, Optional
import tree_sitter as ts

# Try to import language parsers (optional - will fall back to regex if not available)
try:
    import tree_sitter_python as tspython
    PYTHON_LANGUAGE = tspython.language()
except (ImportError, AttributeError):
    PYTHON_LANGUAGE = None

try:
    import tree_sitter_javascript as tsjavascript
    JAVASCRIPT_LANGUAGE = tsjavascript.language()
except (ImportError, AttributeError):
    JAVASCRIPT_LANGUAGE = None

try:
    import tree_sitter_typescript as tstypescript
    TYPESCRIPT_LANGUAGE = tstypescript.language_typescript()
except (ImportError, AttributeError):
    TYPESCRIPT_LANGUAGE = None

try:
    import tree_sitter_go as tsgo
    GO_LANGUAGE = tsgo.language()
except (ImportError, AttributeError):
    GO_LANGUAGE = None


def _get_language_parser(file_path: Path) -> Optional[tuple]:
    """Get tree-sitter parser for file based on extension"""
    ext = file_path.suffix.lower()
    
    language_map = {
        '.py': ('python', PYTHON_LANGUAGE),
        '.js': ('javascript', JAVASCRIPT_LANGUAGE),
        '.jsx': ('javascript', JAVASCRIPT_LANGUAGE),
        '.ts': ('typescript', TYPESCRIPT_LANGUAGE),
        '.tsx': ('typescript', TYPESCRIPT_LANGUAGE),
        '.go': ('go', GO_LANGUAGE),
    }
    
    lang_info = language_map.get(ext)
    if lang_info and lang_info[1] is not None:
        parser = ts.Parser()
        parser.set_language(lang_info[1])
        return (lang_info[0], parser)
    
    return None


def _extract_string_literal(node) -> Optional[str]:
    """Extract string literal from AST node"""
    if node.type == 'string':
        # Remove quotes
        text = node.text.decode('utf-8')
        if text.startswith('"') and text.endswith('"'):
            return text[1:-1]
        elif text.startswith("'") and text.endswith("'"):
            return text[1:-1]
        elif text.startswith('"""') and text.endswith('"""'):
            return text[3:-3]
        elif text.startswith("'''") and text.endswith("'''"):
            return text[3:-3]
    elif node.type == 'concatenated_string':
        # Handle string concatenation
        parts = []
        for child in node.children:
            if child.type == 'string':
                part = _extract_string_literal(child)
                if part:
                    parts.append(part)
        return ''.join(parts) if parts else None
    return None


def _find_python_api_calls_ast(tree, content: bytes) -> List[Dict]:
    """Find Python API calls using AST (requests, httpx, aiohttp)"""
    api_calls = []
    root = tree.root_node
    content_str = content.decode('utf-8')
    
    def traverse(node):
        # requests.get, requests.post, etc.
        if node.type == 'call':
            if node.child_count > 0:
                callee = node.children[0]
                if callee.type == 'attribute':
                    # Check if it's requests.get, httpx.get, etc.
                    obj_text = callee.children[0].text.decode('utf-8') if callee.child_count > 0 else ''
                    method_text = callee.children[-1].text.decode('utf-8') if callee.child_count > 1 else ''
                    
                    if obj_text in ['requests', 'httpx', 'aiohttp'] and method_text in ['get', 'post', 'put', 'delete', 'patch']:
                        # Get URL from first argument
                        if node.child_count > 1:
                            args = node.children[1]
                            if args.type == 'argument_list' and args.child_count > 0:
                                first_arg = args.children[0]
                                url = _extract_string_literal(first_arg)
                                
                                if url:
                                    line_num = node.start_point[0] + 1
                                    api_calls.append({
                                        "method": method_text.upper(),
                                        "url": url,
                                        "file": "",
                                        "line": line_num,
                                        "library": obj_text,
                                        "type": "api_call"
                                    })
        
        for child in node.children:
            traverse(child)
    
    traverse(root)
    return api_calls


def _find_javascript_api_calls_ast(tree, content: bytes) -> List[Dict]:
    """Find JavaScript/TypeScript API calls using AST (fetch, axios, etc.)"""
    api_calls = []
    root = tree.root_node
    content_str = content.decode('utf-8')
    
    def traverse(node):
        # fetch() calls
        if node.type == 'call_expression':
            if node.child_count > 0:
                callee = node.children[0]
                
                # fetch(url)
                if callee.type == 'identifier' and callee.text.decode('utf-8') == 'fetch':
                    if node.child_count > 1:
                        args = node.children[1]
                        if args.type == 'arguments' and args.child_count > 0:
                            first_arg = args.children[0]
                            url = _extract_string_literal(first_arg)
                            
                            if url:
                                # Check for method in second argument (options object)
                                method = 'GET'  # Default
                                if args.child_count > 1:
                                    options = args.children[1]
                                    if options.type == 'object':
                                        for prop in options.children:
                                            if prop.type == 'pair':
                                                key = prop.children[0].text.decode('utf-8') if prop.child_count > 0 else ''
                                                if 'method' in key.lower() and prop.child_count > 1:
                                                    method_val = _extract_string_literal(prop.children[1])
                                                    if method_val:
                                                        method = method_val.upper()
                                
                                line_num = node.start_point[0] + 1
                                api_calls.append({
                                    "method": method,
                                    "url": url,
                                    "file": "",
                                    "line": line_num,
                                    "library": "fetch",
                                    "type": "api_call"
                                })
                
                # axios.get, axios.post, etc.
                elif callee.type == 'member_expression':
                    obj_text = callee.children[0].text.decode('utf-8') if callee.child_count > 0 else ''
                    method_text = callee.children[-1].text.decode('utf-8') if callee.child_count > 1 else ''
                    
                    if obj_text in ['axios', 'http', 'request'] and method_text in ['get', 'post', 'put', 'delete', 'patch']:
                        # Get URL from first argument
                        if node.child_count > 1:
                            args = node.children[1]
                            if args.type == 'arguments' and args.child_count > 0:
                                first_arg = args.children[0]
                                url = _extract_string_literal(first_arg)
                                
                                if url:
                                    line_num = node.start_point[0] + 1
                                    api_calls.append({
                                        "method": method_text.upper(),
                                        "url": url,
                                        "file": "",
                                        "line": line_num,
                                        "library": obj_text,
                                        "type": "api_call"
                                    })
        
        for child in node.children:
            traverse(child)
    
    traverse(root)
    return api_calls


def _find_go_api_calls_ast(tree, content: bytes) -> List[Dict]:
    """Find Go API calls using AST (http.Get, http.Post, etc.)"""
    api_calls = []
    root = tree.root_node
    content_str = content.decode('utf-8')
    
    def traverse(node):
        # http.Get, http.Post, etc.
        if node.type == 'call_expression':
            if node.child_count >= 2:
                callee = node.children[0]
                if callee.type == 'selector_expression':
                    # Check for http.Get, http.Post, etc.
                    obj_text = callee.children[0].text.decode('utf-8') if callee.child_count > 0 else ''
                    method_text = callee.children[-1].text.decode('utf-8') if callee.child_count > 1 else ''
                    
                    if obj_text == 'http' and method_text in ['Get', 'Post', 'Put', 'Delete']:
                        # Get URL from first argument
                        if node.child_count > 1:
                            args = node.children[1]
                            if args.type == 'argument_list' and args.child_count > 0:
                                first_arg = args.children[0]
                                url = _extract_string_literal(first_arg)
                                
                                if url:
                                    line_num = node.start_point[0] + 1
                                    api_calls.append({
                                        "method": method_text.upper(),
                                        "url": url,
                                        "file": "",
                                        "line": line_num,
                                        "library": "http",
                                        "type": "api_call"
                                    })
        
        for child in node.children:
            traverse(child)
    
    traverse(root)
    return api_calls


def _find_api_calls_regex_fallback(file_path: Path, content: str) -> List[Dict]:
    """Fallback to regex-based detection for unsupported languages or AST failures"""
    api_calls = []
    
    # Common API call patterns (regex fallback)
    API_CALL_PATTERNS = [
        # fetch()
        (r'fetch\(["\']([^"\']+)["\']', 'fetch'),
        
        # axios
        (r'axios\.(get|post|put|delete|patch)\(["\']([^"\']+)["\']', 'axios'),
        
        # requests (Python)
        (r'requests\.(get|post|put|delete|patch)\(["\']([^"\']+)["\']', 'requests'),
        
        # http.get/post (Go)
        (r'http\.(Get|Post|Put|Delete)\(["\']([^"\']+)["\']', 'http'),
    ]
    
    for pattern, library in API_CALL_PATTERNS:
        matches = re.finditer(pattern, content, re.IGNORECASE)
        
        for match in matches:
            if library == 'fetch':
                method = 'GET'  # Default for fetch
                url = match.group(1)
            else:
                method = match.group(1).upper()
                url = match.group(2)
            
            line_num = content[:match.start()].count('\n') + 1
            
            api_calls.append({
                "method": method,
                "url": url,
                "file": str(file_path),
                "line": line_num,
                "library": library,
                "type": "api_call"
            })
    
    return api_calls


def find_api_calls(file_path: Path, content: str) -> List[Dict]:
    """
    Find API calls in file content using AST parsing with regex fallback
    
    Args:
        file_path: Path to file
        content: File content
        
    Returns:
        List of API call dictionaries
    """
    api_calls = []
    
    # Try AST parsing first
    try:
        parser_info = _get_language_parser(file_path)
        if parser_info:
            language, parser = parser_info
            tree = parser.parse(bytes(content, 'utf-8'))
            
            if language == 'python':
                api_calls = _find_python_api_calls_ast(tree, bytes(content, 'utf-8'))
            elif language in ['javascript', 'typescript']:
                api_calls = _find_javascript_api_calls_ast(tree, bytes(content, 'utf-8'))
            elif language == 'go':
                api_calls = _find_go_api_calls_ast(tree, bytes(content, 'utf-8'))
            
            # Set file path for all API calls
            for api_call in api_calls:
                api_call['file'] = str(file_path)
    except Exception:
        # AST parsing failed, fall back to regex
        pass
    
    # If AST parsing found nothing or failed, use regex fallback
    if not api_calls:
        api_calls = _find_api_calls_regex_fallback(file_path, content)
    
    return api_calls
