"""The Kentik service is used for external interactions with Kentik."""

import logging
from typing import Any

import requests

from gso.products.product_types.router import Router
from gso.settings import load_oss_params

logger = logging.getLogger(__name__)


class KentikClient:
    """The client for Kentik that interacts with an external instance."""

    def __init__(self) -> None:
        """Instantiate a new Kentik Client."""
        self.config = load_oss_params().KENTIK
        self.headers = {
            "X-CH-Auth-Email": self.config.user_email,
            "X-CH-Auth-API-Token": self.config.api_key,
            "Content-Type": "application/json",
        }

    def _send_request(self, method, endpoint: str, data: dict[str, Any] | None = None) -> dict[str, Any]:
        url = self.config.api_base + endpoint
        result = requests.request(method, url, json=data, headers=self.headers)

        return result.json()

    def get_devices(self) -> list[dict[str, Any]]:
        """List all devices in Kentik."""
        return [self._send_request("GET", "v5/devices")]

    def get_device(self, device_id: str) -> dict[str, Any]:
        """Get a device by ID."""
        return self._send_request("GET", f"v5/device/{device_id}")

    def get_device_by_name(self, device_name: str) -> dict[str, Any]:
        """Fetch a device in Kentik by its :term:`FQDN`.

        If the device is not found, returns an empty dict.

        :param str device_name: The :term:`FQDN` of the sought device.
        """
        devices = self.get_devices()
        for device in devices:
            if device["name"] == device_name:
                return device

        return {}

    def get_sites(self) -> list[dict[str, Any]]:
        """Get a list of all available sites in Kentik."""
        return self._send_request("GET", "v5/sites")["sites"]

    def get_site(self, site_id: str) -> dict[str, Any]:
        """Get a site by ID."""
        return self._send_request("GET", f"v5/site/{site_id}")

    def get_site_by_name(self, site_slug: str) -> dict[str, Any]:
        """Get a Kentik site by its name.

        If the site is not found, return an empty dict.

        :param str site_slug: The name of the site, should be a three-letter slug like COR or POZ.
        """
        sites = self.get_sites()
        for site in sites:
            if site["site_name"] == site_slug:
                return site

        return {}

    def get_plans(self) -> list[dict[str, Any]]:
        """Get all Kentik plans available."""
        return self._send_request("GET", "v5/plans")["plans"]

    def get_plan_by_name(self, plan_name: str) -> dict[str, Any]:
        """Get a Kentik plan by its name.

        If the plan is not found, returns an empty dict.

        :param str plan_name: The name of the plan.
        """
        plans = self.get_plans()
        for plan in plans:
            if plan["name"] == plan_name:
                return plan

        return {}

    def create_device(self, router: Router) -> dict[str, Any]:
        """Add a new device to Kentik."""
        site_id = self.get_site_by_name(router.router.router_site.site_name)["id"]
        plan_id = self.get_plan_by_name(self.config.billing_plans[router.router.router_site.site_tier])
        request_body = {
            "device": {
                # Route selection missing
                "deviceName": router.router.router_fqdn,
                "deviceSubtype": self.config.device_type,
                "sendingIps": [str(router.router.router_lo_ipv4_address)],
                "deviceSampleRate": self.config.sample_rate,
                "planId": plan_id,
                "siteId": site_id,
                "deviceSnmpIp": str(router.router.router_lo_ipv4_address),
                "deviceSnmpCommunity": self.config.snmp_community,
                "deviceBgpType": self.config.bgp_type,
                "deviceBgpFlowspec": False,
                "deviceBgpNeighborIp": str(router.router.router_lo_ipv4_address),
                "deviceBgpNeighborIp6": str(router.router.router_lo_ipv6_address),
                "deviceBgpNeighborAsn": str(self.config.ASN),
                "deviceBgpPassword": self.config.md5_password,
            }
        }

        return self._send_request("POST", "v5/device", request_body)

    def remove_device(self, hostname: str) -> None:
        """Remove a device from Kentik."""