"""
SystemVerilog language server using verible-verilog-ls.
"""

import logging
import os
import shutil
import subprocess
from typing import Any

from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer
from solidlsp.ls_config import LanguageServerConfig
from solidlsp.settings import SolidLSPSettings

from .common import RuntimeDependency, RuntimeDependencyCollection

log = logging.getLogger(__name__)

VERIBLE_ALLOWED_HOSTS = ("github.com", "release-assets.githubusercontent.com", "objects.githubusercontent.com")


class SystemVerilogLanguageServer(SolidLanguageServer):
    """
    SystemVerilog language server using verible-verilog-ls.
    Supports .sv, .svh, .v, .vh files.

    You can pass the following entries in ``ls_specific_settings["systemverilog"]``:
        - verible_version: Override the pinned Verible release version downloaded
          by Serena (default: the bundled Serena version).
    """

    def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings) -> None:
        super().__init__(config, repository_root_path, None, "systemverilog", solidlsp_settings)

    def _create_dependency_provider(self) -> LanguageServerDependencyProvider:
        return self.DependencyProvider(self._custom_settings, self._ls_resources_dir)

    class DependencyProvider(LanguageServerDependencyProviderSinglePath):
        def _get_or_install_core_dependency(self) -> str:
            # 1. Check PATH first for system-installed verible
            system_verible = shutil.which("verible-verilog-ls")
            if system_verible:
                # Log version information
                try:
                    result = subprocess.run(
                        [system_verible, "--version"],
                        capture_output=True,
                        text=True,
                        check=False,
                        timeout=5,
                    )
                    if result.returncode == 0:
                        version_info = result.stdout.strip().split("\n")[0]
                        log.info(f"Using system-installed verible-verilog-ls: {version_info}")
                    else:
                        log.info(f"Using system-installed verible-verilog-ls at {system_verible}")
                except Exception:
                    log.info(f"Using system-installed verible-verilog-ls at {system_verible}")
                return system_verible

            # 2. Not found in PATH, try to download
            verible_version = self._custom_settings.get("verible_version", "v0.0-4051-g9fdb4057")
            base_url = f"https://github.com/chipsalliance/verible/releases/download/{verible_version}"

            deps = RuntimeDependencyCollection(
                [
                    RuntimeDependency(
                        id="verible-ls",
                        description="verible-verilog-ls for Linux (x64)",
                        url=f"{base_url}/verible-{verible_version}-linux-static-x86_64.tar.gz",
                        platform_id="linux-x64",
                        archive_type="gztar",
                        binary_name=f"verible-{verible_version}/bin/verible-verilog-ls",
                        sha256="f52e5920ef63f70620a6086e09dea8bd778147cd7a9ff827bb7de5d6316b1754",
                        allowed_hosts=VERIBLE_ALLOWED_HOSTS,
                    ),
                    RuntimeDependency(
                        id="verible-ls",
                        description="verible-verilog-ls for Linux (arm64)",
                        url=f"{base_url}/verible-{verible_version}-linux-static-arm64.tar.gz",
                        platform_id="linux-arm64",
                        archive_type="gztar",
                        binary_name=f"verible-{verible_version}/bin/verible-verilog-ls",
                        sha256="30dd9c6f6e0f4840d6ba0c9e81ea2774a50b5a1a523a855245f9a9b4beb6b58b",
                        allowed_hosts=VERIBLE_ALLOWED_HOSTS,
                    ),
                    RuntimeDependency(
                        id="verible-ls",
                        description="verible-verilog-ls for macOS",
                        url=f"{base_url}/verible-{verible_version}-macOS.tar.gz",
                        platform_id="osx-x64",
                        archive_type="gztar",
                        binary_name=f"verible-{verible_version}/bin/verible-verilog-ls",
                        sha256="9ef92e9ad345285dd593763e10ca61c8532fcf47bbb6cf4448f9a9423882d662",
                        allowed_hosts=VERIBLE_ALLOWED_HOSTS,
                    ),
                    RuntimeDependency(
                        id="verible-ls",
                        description="verible-verilog-ls for macOS",
                        url=f"{base_url}/verible-{verible_version}-macOS.tar.gz",
                        platform_id="osx-arm64",
                        archive_type="gztar",
                        binary_name=f"verible-{verible_version}/bin/verible-verilog-ls",
                        sha256="9ef92e9ad345285dd593763e10ca61c8532fcf47bbb6cf4448f9a9423882d662",
                        allowed_hosts=VERIBLE_ALLOWED_HOSTS,
                    ),
                    RuntimeDependency(
                        id="verible-ls",
                        description="verible-verilog-ls for Windows (x64)",
                        url=f"{base_url}/verible-{verible_version}-win64.zip",
                        platform_id="win-x64",
                        archive_type="zip",
                        binary_name=f"verible-{verible_version}/bin/verible-verilog-ls.exe",
                        sha256="729aa244036da4a4f87bc026d33555456fc7f7be79778d983ebe9c893f4a0ca3",
                        allowed_hosts=VERIBLE_ALLOWED_HOSTS,
                    ),
                ]
            )

            try:
                dep = deps.get_single_dep_for_current_platform()
            except RuntimeError:
                dep = None

            if dep is None:
                raise FileNotFoundError(
                    "verible-verilog-ls is not installed on your system.\n"
                    + "Please install verible using one of the following methods:\n"
                    + "  conda:      conda install -c conda-forge verible\n"
                    + "  Homebrew:   brew install verible\n"
                    + "  GitHub:     Download from https://github.com/chipsalliance/verible/releases\n"
                    + "See https://github.com/chipsalliance/verible for more details."
                )

            verible_ls_dir = os.path.join(self._ls_resources_dir, "verible-ls")
            executable_path = deps.binary_path(verible_ls_dir)

            if not os.path.exists(executable_path):
                log.info(f"verible-verilog-ls not found. Downloading from {dep.url}")
                _ = deps.install(verible_ls_dir)

            if not os.path.exists(executable_path):
                raise FileNotFoundError(f"verible-verilog-ls not found at {executable_path}")

            os.chmod(executable_path, 0o755)
            return executable_path

        def _create_launch_command(self, core_path: str) -> list[str]:
            return [core_path]

    def _create_base_initialize_params(self) -> dict:
        initialize_params = {
            "locale": "en",
            "capabilities": {
                "textDocument": {
                    "synchronization": {"didSave": True, "dynamicRegistration": True},
                    "completion": {
                        "dynamicRegistration": True,
                        "completionItem": {"snippetSupport": True},
                    },
                    "definition": {"dynamicRegistration": True},
                    "references": {"dynamicRegistration": True},
                    "hover": {
                        "dynamicRegistration": True,
                        "contentFormat": ["markdown", "plaintext"],
                    },
                    "documentSymbol": {
                        "dynamicRegistration": True,
                        "hierarchicalDocumentSymbolSupport": True,
                        "symbolKind": {"valueSet": list(range(1, 27))},
                    },
                    "codeAction": {"dynamicRegistration": True},
                    "formatting": {"dynamicRegistration": True},
                    "documentHighlight": {"dynamicRegistration": True},
                    "publishDiagnostics": {"relatedInformation": True},
                },
                "workspace": {
                    "workspaceFolders": True,
                    "didChangeConfiguration": {"dynamicRegistration": True},
                },
            },
        }
        return initialize_params

    def _start_server(self) -> None:
        def do_nothing(params: Any) -> None:
            return

        def on_log_message(params: Any) -> None:
            message = params.get("message", "") if isinstance(params, dict) else str(params)
            log.info(f"verible-verilog-ls: {message}")

        self.server.on_request("client/registerCapability", do_nothing)
        self.server.on_notification("$/progress", do_nothing)
        self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
        self.server.on_notification("window/logMessage", on_log_message)

        log.info("Starting verible-verilog-ls process")
        self.server.start()
        initialize_params = self._create_initialize_params()

        log.info("Sending initialize request")
        init_response = self.server.send.initialize(initialize_params)

        # Validate server capabilities (follows Gopls/Bash pattern)
        capabilities = init_response.get("capabilities", {})
        log.info(f"Initialize response capabilities: {list(capabilities.keys())}")
        assert "textDocumentSync" in capabilities, "verible-verilog-ls must support textDocumentSync"
        if "documentSymbolProvider" not in capabilities:
            log.warning("verible-verilog-ls does not advertise documentSymbolProvider")
        if "definitionProvider" not in capabilities:
            log.warning("verible-verilog-ls does not advertise definitionProvider")

        self.server.notify.initialized({})
