"""
Endpoint detection - find API endpoints in backend 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

try:
    import tree_sitter_java as tsjava
    JAVA_LANGUAGE = tsjava.language()
except (ImportError, AttributeError):
    JAVA_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),
        '.java': ('java', JAVA_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_endpoints_ast(tree, content: bytes) -> List[Dict]:
    """Find Python endpoints using AST (Flask, FastAPI, Django)"""
    endpoints = []
    root = tree.root_node
    
    def traverse(node):
        # Flask @app.route decorator
        if node.type == 'decorator':
            decorator_name = None
            path = None
            method = 'GET'
            
            # Get decorator name
            if node.child_count > 0:
                first_child = node.children[0]
                if first_child.type == 'attribute':
                    # @app.route or @router.get
                    attr_text = first_child.text.decode('utf-8')
                    if 'route' in attr_text:
                        decorator_name = 'route'
                    elif any(m in attr_text for m in ['get', 'post', 'put', 'delete', 'patch']):
                        method = attr_text.split('.')[-1].upper()
                        decorator_name = 'method'
                
                # Extract path from arguments
                if node.child_count > 1:
                    call_node = node.children[1]
                    if call_node.type == 'call':
                        # First argument is usually the path
                        if call_node.child_count > 1:
                            args = call_node.children[1]  # argument_list
                            if args.child_count > 0:
                                first_arg = args.children[0]
                                path = _extract_string_literal(first_arg)
                                
                                # Check for methods parameter
                                for i in range(1, args.child_count):
                                    arg = args.children[i]
                                    if arg.type == 'keyword_argument':
                                        if arg.children[0].text.decode('utf-8') == 'methods':
                                            # Extract methods from list
                                            if arg.child_count > 1:
                                                methods_node = arg.children[1]
                                                if methods_node.type == 'list':
                                                    if methods_node.child_count > 0:
                                                        method_node = methods_node.children[0]
                                                        method = _extract_string_literal(method_node)
                                                        if method:
                                                            method = method.upper()
            
            if decorator_name and path:
                line_num = node.start_point[0] + 1
                endpoints.append({
                    "method": method,
                    "path": path,
                    "file": "",  # Will be set by caller
                    "line": line_num,
                    "language": "python",
                    "type": "endpoint",
                    "framework": "flask" if decorator_name == 'route' else "fastapi"
                })
        
        # FastAPI @app.get, @app.post, etc.
        if node.type == 'decorator':
            decorator_text = node.text.decode('utf-8')
            if any(method in decorator_text for method in ['@app.', '@router.']):
                for method_name in ['get', 'post', 'put', 'delete', 'patch']:
                    pattern = rf'@(?:app|router)\.{method_name}\((["\'])([^"\']+)\1'
                    match = re.search(pattern, decorator_text)
                    if match:
                        path = match.group(2)
                        line_num = node.start_point[0] + 1
                        endpoints.append({
                            "method": method_name.upper(),
                            "path": path,
                            "file": "",
                            "line": line_num,
                            "language": "python",
                            "type": "endpoint",
                            "framework": "fastapi"
                        })
                        break
        
        for child in node.children:
            traverse(child)
    
    traverse(root)
    return endpoints


def _find_javascript_endpoints_ast(tree, content: bytes) -> List[Dict]:
    """Find JavaScript/TypeScript endpoints using AST (Express, Next.js)"""
    endpoints = []
    root = tree.root_node
    content_str = content.decode('utf-8')
    
    def traverse(node):
        # Express app.get/post/etc.
        if node.type == 'call_expression':
            if node.child_count >= 2:
                callee = node.children[0]
                if callee.type == 'member_expression':
                    # Check if it's app.get, router.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 in ['app', 'router', 'express'] and method_text in ['get', 'post', 'put', 'delete', 'patch']:
                        # Get path 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]
                                path = _extract_string_literal(first_arg)
                                
                                if path:
                                    line_num = node.start_point[0] + 1
                                    endpoints.append({
                                        "method": method_text.upper(),
                                        "path": path,
                                        "file": "",
                                        "line": line_num,
                                        "language": "javascript",
                                        "type": "endpoint",
                                        "framework": "express"
                                    })
        
        for child in node.children:
            traverse(child)
    
    traverse(root)
    return endpoints


def _find_go_endpoints_ast(tree, content: bytes) -> List[Dict]:
    """Find Go endpoints using AST"""
    endpoints = []
    root = tree.root_node
    content_str = content.decode('utf-8')
    
    def traverse(node):
        # Go router methods: router.Get, router.Post, etc.
        if node.type == 'call_expression':
            if node.child_count >= 2:
                callee = node.children[0]
                if callee.type == 'selector_expression':
                    # Check for router.Get, http.HandleFunc, etc.
                    method_text = callee.children[-1].text.decode('utf-8') if callee.child_count > 0 else ''
                    
                    if method_text in ['Get', 'Post', 'Put', 'Delete', 'Patch', 'HandleFunc']:
                        # Get path 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]
                                path = _extract_string_literal(first_arg)
                                
                                if path:
                                    method = method_text.upper() if method_text != 'HandleFunc' else 'GET'
                                    line_num = node.start_point[0] + 1
                                    endpoints.append({
                                        "method": method,
                                        "path": path,
                                        "file": "",
                                        "line": line_num,
                                        "language": "go",
                                        "type": "endpoint",
                                        "framework": "net/http"
                                    })
        
        for child in node.children:
            traverse(child)
    
    traverse(root)
    return endpoints


def _find_java_endpoints_ast(tree, content: bytes) -> List[Dict]:
    """Find Java endpoints using AST (Spring Boot)"""
    endpoints = []
    root = tree.root_node
    content_str = content.decode('utf-8')
    
    def traverse(node):
        # Spring annotations: @GetMapping, @PostMapping, etc.
        if node.type == 'annotation':
            annotation_text = node.text.decode('utf-8')
            method_map = {
                'GetMapping': 'GET',
                'PostMapping': 'POST',
                'PutMapping': 'PUT',
                'DeleteMapping': 'DELETE',
                'PatchMapping': 'PATCH',
                'RequestMapping': 'GET'  # Default
            }
            
            for annotation_name, method in method_map.items():
                if annotation_name in annotation_text:
                    # Extract path from annotation
                    pattern = rf'@(?:{annotation_name}|RequestMapping)\(["\']([^"\']+)["\']'
                    match = re.search(pattern, annotation_text)
                    if match:
                        path = match.group(1)
                        line_num = node.start_point[0] + 1
                        endpoints.append({
                            "method": method,
                            "path": path,
                            "file": "",
                            "line": line_num,
                            "language": "java",
                            "type": "endpoint",
                            "framework": "spring"
                        })
                    break
        
        for child in node.children:
            traverse(child)
    
    traverse(root)
    return endpoints


def _find_endpoints_regex_fallback(file_path: Path, content: str) -> List[Dict]:
    """Fallback to regex-based detection for unsupported languages or AST failures"""
    endpoints = []
    
    # Common endpoint patterns (regex fallback)
    ENDPOINT_PATTERNS = [
        # Python Flask @app.route
        (r'@app\.route\(["\']([^"\']+)["\'](?:.*?methods\s*=\s*\[["\']([^"\']+)["\'])?', 'python_flask_route'),
        
        # Python Flask/FastAPI method decorators
        (r'@app\.(get|post|put|delete|patch)\(["\']([^"\']+)["\']', 'python'),
        (r'@router\.(get|post|put|delete|patch)\(["\']([^"\']+)["\']', 'python'),
        
        # JavaScript/TypeScript Express
        (r'app\.(get|post|put|delete|patch)\(["\']([^"\']+)["\']', 'javascript'),
        (r'router\.(get|post|put|delete|patch)\(["\']([^"\']+)["\']', 'javascript'),
        
        # Go
        (r'(GET|POST|PUT|DELETE|PATCH)\(["\']([^"\']+)["\']', 'go'),
        
        # Java Spring
        (r'@(GetMapping|PostMapping|PutMapping|DeleteMapping|PatchMapping)\(["\']([^"\']+)["\']', 'java'),
    ]
    
    for pattern, language in ENDPOINT_PATTERNS:
        matches = re.finditer(pattern, content, re.IGNORECASE | re.DOTALL)
        
        for match in matches:
            if language == 'python_flask_route':
                path = match.group(1)
                method = match.group(2) if match.group(2) else 'GET'
                method = method.upper()
            else:
                method = match.group(1).upper()
                path = match.group(2)
            
            line_num = content[:match.start()].count('\n') + 1
            
            endpoints.append({
                "method": method,
                "path": path,
                "file": str(file_path),
                "line": line_num,
                "language": language.replace('_flask_route', ''),
                "type": "endpoint"
            })
    
    return endpoints


def find_endpoints(file_path: Path, content: str) -> List[Dict]:
    """
    Find API endpoints in file content using AST parsing with regex fallback
    
    Args:
        file_path: Path to file
        content: File content
        
    Returns:
        List of endpoint dictionaries
    """
    endpoints = []
    
    # 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':
                endpoints = _find_python_endpoints_ast(tree, bytes(content, 'utf-8'))
            elif language in ['javascript', 'typescript']:
                endpoints = _find_javascript_endpoints_ast(tree, bytes(content, 'utf-8'))
            elif language == 'go':
                endpoints = _find_go_endpoints_ast(tree, bytes(content, 'utf-8'))
            elif language == 'java':
                endpoints = _find_java_endpoints_ast(tree, bytes(content, 'utf-8'))
            
            # Set file path for all endpoints
            for endpoint in endpoints:
                endpoint['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 endpoints:
        endpoints = _find_endpoints_regex_fallback(file_path, content)
    
    return endpoints
