import html
from typing import List, Dict
from schemathesis import checks
from schemathesis.specs.openapi import checks as oai_checks
from schemathesis.checks import CheckContext, ChecksConfig
from urllib.parse import unquote
from ..seed import SeedManager

def run_drift_check(schema, base_url: str, api_key: str, seed_manager: SeedManager) -> List[Dict]:
    """
    Module A: The 'Docs vs. Code' Drift Check (The Integrity Test)
    Uses schemathesis to verify if the API implementation matches the spec.
    """
    results = []
    # Mapping check names to actual functions
    check_map = {
        "not_a_server_error": checks.not_a_server_error,
        "status_code_conformance": oai_checks.status_code_conformance,
        "response_schema_conformance": oai_checks.response_schema_conformance
    }
    check_names = list(check_map.keys())
    
    # Schemathesis 4.x checks require a context object
    checks_config = ChecksConfig()
    check_ctx = CheckContext(
        override=None,
        auth=None,
        headers=None,
        config=checks_config,
        transport_kwargs=None,
    )
    
    for op in schema.get_all_operations():
        # Handle Result type (Ok/Err) wrapping if present
        operation = op.ok() if hasattr(op, "ok") else op
        
        try:
            # Generate test case
            try:
                case = operation.as_strategy().example()
            except (AttributeError, Exception):
                try:
                    cases = list(operation.make_case())
                    case = cases[0] if cases else None
                except (AttributeError, Exception):
                    case = None
            
            if not case:
                continue

            seeded_keys = seed_manager.apply_seed_data(case) or set()

            formatted_path = operation.path
            if case.path_parameters:
                for key, value in case.path_parameters.items():
                        if key in seeded_keys:
                            display_value = unquote(str(value))
                        else:
                            display_value = "random"
                        
                        formatted_path = formatted_path.replace(f"{{{key}}}", f"{{{key}:{display_value}}}")
            
            print(f"AUDIT LOG: Testing endpoint {operation.method.upper()} {formatted_path}")

            headers = {}
            if api_key:
                auth_header = api_key if api_key.lower().startswith("bearer ") else f"Bearer {api_key}"
                headers["Authorization"] = auth_header

            # Call the API
            target_url = f"{base_url.rstrip('/')}/{formatted_path.lstrip('/')}"
            print(f"AUDIT LOG: Calling {operation.method.upper()} {target_url}")
            
            response = case.call(base_url=base_url, headers=headers)
            print(f"AUDIT LOG: Response Status Code: {response.status_code}")
            
            # We manually call the check function to ensure arguments are passed correctly.
            for check_name in check_names:
                check_func = check_map[check_name]
                try:
                    # Direct call: check_func(ctx, response, case)
                    check_func(check_ctx, response, case)
                    
                    # If we get here, the check passed
                    results.append({
                        "module": "A",
                        "endpoint": f"{operation.method.upper()} {operation.path}",
                        "issue": f"{check_name} - Passed",
                        "status": "PASS",
                        "severity": "INFO",
                        "details": f"Status: {response.status_code}"
                    })

                except AssertionError as e:
                    # This catches actual drift (e.g., Schema validation failed)
                    # Capture and format detailed error info
                    validation_errors = []
                    
                    # Safely get causes if they exist and are iterable
                    causes = getattr(e, "causes", None)
                    if causes:
                        for cause in causes:
                            if hasattr(cause, "message"):
                                validation_errors.append(cause.message)
                            else:
                                validation_errors.append(str(cause))
                    
                    if not validation_errors:
                        validation_errors.append(str(e) or "Validation failed")
                    
                    err_msg = "<br>".join(validation_errors)
                    safe_err = html.escape(err_msg)
                    
                    # Add helpful context (Status & Body Preview)
                    context_msg = f"Status: {response.status_code}"
                    try:
                        if response.content:
                            preview = response.text[:500]
                            safe_preview = html.escape(preview)
                            context_msg += f"<br>Response: {safe_preview}"
                    except Exception:
                        pass
                        
                    full_details = f"<strong>Error:</strong> {safe_err}<br><br><strong>Context:</strong><br>{context_msg}"

                    print(f"AUDIT LOG: Validation {check_name} failed: {err_msg}")
                    results.append({
                        "module": "A",
                        "endpoint": f"{operation.method.upper()} {operation.path}",
                        "issue": f"Schema Drift Detected ({check_name})",
                        "status": "FAIL",
                        "details": full_details,
                        "severity": "HIGH"
                    })
                except Exception as e:
                    # This catches unexpected coding errors
                    print(f"AUDIT LOG: Error executing check {check_name}: {str(e)}")
                    results.append({
                        "module": "A",
                        "endpoint": f"{operation.method.upper()} {operation.path}",
                        "issue": f"Check Execution Error ({check_name})",
                        "status": "FAIL",
                        "details": str(e),
                        "severity": "HIGH"
                    })
                    
        except Exception as e:
            print(f"AUDIT LOG: Critical Error during endpoint test: {str(e)}")
            continue
            
    return results
