"""
WebView2Loader.dll discovery and loading utilities.

This module handles finding and loading the WebView2Loader.dll,
with support for various installation locations and lazy loading.
"""

import ctypes
import os
from ctypes import wintypes
from typing import Optional

from comtypes import HRESULT

from .._internal.logging import debug_log
from ..exceptions import WebViewNotAvailableError
from .interfaces import ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler

_MODULE = "loader"

# Cached loader instance
_webview2_loader: Optional[ctypes.WinDLL] = None


def _pick_newest_version_dir(base_path: str) -> str:
    """Find the newest version directory in a path."""
    if not os.path.isdir(base_path):
        return ""
    version_dirs = [
        entry
        for entry in os.listdir(base_path)
        if os.path.isdir(os.path.join(base_path, entry))
    ]
    if not version_dirs:
        return ""
    version_dirs.sort(reverse=True)
    return os.path.join(base_path, version_dirs[0])


def _first_existing_path(paths) -> str:
    """Return the first path that exists."""
    for path in paths:
        if path and os.path.exists(path):
            return path
    return ""


def _resolve_loader_path() -> str:
    """
    Resolve the path to WebView2Loader.dll.

    Checks in order:
    1. Environment variable override (WEBVIEW2_LOADER_PATH)
    2. Current directory or package directory
    3. Common install locations (Office/EdgeWebView/Adobe)
    4. Versioned EdgeWebView application directories
    5. System PATH as last resort
    """
    # 1) Env override
    env_path = os.environ.get("WEBVIEW2_LOADER_PATH", "").strip()
    if env_path and os.path.exists(env_path):
        return env_path

    # 2) Current directory or package directory
    local_paths = [
        "WebView2Loader.dll",
        os.path.join(os.path.dirname(os.path.dirname(__file__)), "WebView2Loader.dll"),
    ]
    found = _first_existing_path(local_paths)
    if found:
        return found

    # 3) Common install locations (Office/EdgeWebView/Adobe)
    candidates = []
    common_program_files = [
        os.environ.get("CommonProgramFiles", ""),
        os.environ.get("CommonProgramFiles(x86)", ""),
    ]
    program_files = [
        os.environ.get("ProgramFiles", ""),
        os.environ.get("ProgramFiles(x86)", ""),
        os.environ.get("ProgramW6432", ""),
    ]

    for base in common_program_files:
        candidates.extend(
            [
                os.path.join(base, "Microsoft", "EdgeWebView", "WebView2Loader.dll"),
                os.path.join(
                    base, "Adobe", "Microsoft", "EdgeWebView", "WebView2Loader.dll"
                ),
            ]
        )

    for base in program_files:
        candidates.extend(
            [
                os.path.join(
                    base, "Microsoft Office", "root", "Office16", "WebView2Loader.dll"
                ),
            ]
        )

    found = _first_existing_path(candidates)
    if found:
        return found

    # 4) Versioned EdgeWebView application directories
    for base in program_files:
        app_root = os.path.join(base, "Microsoft", "EdgeWebView", "Application")
        newest = _pick_newest_version_dir(app_root)
        if newest:
            candidate = os.path.join(newest, "WebView2Loader.dll")
            if os.path.exists(candidate):
                return candidate

    # 5) If not found, try system path as a last resort
    return "WebView2Loader.dll"


def get_loader() -> ctypes.WinDLL:
    """
    Get the WebView2Loader.dll instance, loading it if necessary.

    This function implements lazy loading - the DLL is only loaded
    when first needed, not at module import time.

    Returns:
        The loaded WebView2Loader.dll as a WinDLL instance.

    Raises:
        WebViewNotAvailableError: If the loader DLL cannot be found or loaded.
    """
    global _webview2_loader

    if _webview2_loader is not None:
        return _webview2_loader

    try:
        loader_path = _resolve_loader_path()
        debug_log(f"WebView2Loader.dll resolved to: {loader_path}", _MODULE)
        _webview2_loader = ctypes.WinDLL(loader_path)
        return _webview2_loader
    except OSError as e:
        raise WebViewNotAvailableError(
            f"Failed to load WebView2Loader.dll: {e}\n"
            "Please ensure WebView2 runtime is installed.\n"
            "Download from: https://developer.microsoft.com/en-us/microsoft-edge/webview2/"
        ) from e
    except Exception as e:
        raise WebViewNotAvailableError(
            f"Failed to load WebView2Loader.dll: {e}"
        ) from e


def create_environment_with_options(
    handler: ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler,
) -> int:
    """
    Create a WebView2 environment with default options.

    Args:
        handler: The completion handler to invoke when environment creation completes.

    Returns:
        HRESULT code (0 = success).

    Raises:
        WebViewNotAvailableError: If the loader DLL cannot be loaded.
    """
    loader = get_loader()

    try:
        CreateCoreWebView2EnvironmentWithOptions = (
            loader.CreateCoreWebView2EnvironmentWithOptions
        )
    except AttributeError as e:
        raise WebViewNotAvailableError(
            f"Missing CreateCoreWebView2EnvironmentWithOptions in loader DLL: {e}"
        ) from e

    CreateCoreWebView2EnvironmentWithOptions.argtypes = [
        wintypes.LPCWSTR,
        wintypes.LPCWSTR,
        ctypes.c_void_p,
        ctypes.POINTER(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler),
    ]
    CreateCoreWebView2EnvironmentWithOptions.restype = HRESULT

    return CreateCoreWebView2EnvironmentWithOptions(None, None, None, handler)
