from pathlib import Path
from typing import Any

import msgspec.json
import requests

from csirtmu.external_services.netbox.structs import (
    GraphQLInput,
    NetboxIPAddress,
    NetboxPrefix,
    NetboxTenant,
)
from csirtmu.self_service_backend.config import NetboxConfig


class NetboxClient:
    RESPONSE_NOK_ERROR_MSG = "Received non-200 response code."

    def __init__(self, config: NetboxConfig) -> None:
        self.netbox_config = config
        self._queries = (Path(__file__).parent / "queries.graphql").read_text("utf-8")

    def search_ip_addresses(self, query: str) -> list[NetboxIPAddress]:
        operation = "SearchIPAddresses"
        variables = {"ip": query}
        data: dict[str, dict[str, Any]] = self._send_graphql_req(operation, variables)
        return msgspec.convert(
            [
                item | {"url": f"{self.netbox_config.link_url}ipam/ip-addresses/{item['id']}"}
                for item in data["data"]["ip_address_list"]
            ],
            type=list[NetboxIPAddress],
        )

    def search_prefixes(self, query: str) -> list[NetboxPrefix]:
        operation = "SearchPrefixes"
        variables = {"ip": query}
        data: dict[str, dict[str, Any]] = self._send_graphql_req(operation, variables)
        return msgspec.convert(
            [
                item | {"url": f"{self.netbox_config.link_url}ipam/prefixes/{item['id']}"}
                for item in data["data"]["prefix_list"]
            ],
            type=list[NetboxPrefix],
        )

    def search_tenants(self, query: str) -> list[NetboxTenant]:
        operation = "SearchTenants"
        variables = {"name": query}
        data = self._send_graphql_req(operation, variables)
        return msgspec.convert(
            [
                item | {"url": f"{self.netbox_config.link_url}tenancy/tenants/{item['id']}"}
                for item in data["data"]["tenant_list"]
            ],
            type=list[NetboxTenant],
        )

    def _send_graphql_req(self, operation: str, variables: dict[str, Any]) -> dict[str, dict[str, Any]]:
        data = msgspec.json.encode(GraphQLInput(query=self._queries, variables=variables, operation_name=operation))
        response = requests.post(
            self.netbox_config.graphql_url,
            data=data,
            headers=self.netbox_config.auth_headers | {"Content-Type": "application/json"},
            timeout=100,
        )
        if not response.ok:
            raise RuntimeError(self.RESPONSE_NOK_ERROR_MSG)
        return response.json()
