"""Init file for Supervisor RESTful API."""
import logging
from pathlib import Path
from typing import Optional

from aiohttp import web

from ..coresys import CoreSys, CoreSysAttributes
from .addons import APIAddons
from .audio import APIAudio
from .auth import APIAuth
from .cli import APICli
from .discovery import APIDiscovery
from .dns import APICoreDNS
from .docker import APIDocker
from .hardware import APIHardware
from .homeassistant import APIHomeAssistant
from .host import APIHost
from .info import APIInfo
from .ingress import APIIngress
from .jobs import APIJobs
from .middleware.security import SecurityMiddleware
from .multicast import APIMulticast
from .network import APINetwork
from .observer import APIObserver
from .os import APIOS
from .proxy import APIProxy
from .resolution import APIResoulution
from .security import APISecurity
from .services import APIServices
from .snapshots import APISnapshots
from .store import APIStore
from .supervisor import APISupervisor

_LOGGER: logging.Logger = logging.getLogger(__name__)


MAX_CLIENT_SIZE: int = 1024 ** 2 * 16


class RestAPI(CoreSysAttributes):
    """Handle RESTful API for Supervisor."""

    def __init__(self, coresys: CoreSys):
        """Initialize Docker base wrapper."""
        self.coresys: CoreSys = coresys
        self.security: SecurityMiddleware = SecurityMiddleware(coresys)
        self.webapp: web.Application = web.Application(
            client_max_size=MAX_CLIENT_SIZE,
            middlewares=[
                self.security.system_validation,
                self.security.token_validation,
            ],
        )

        # service stuff
        self._runner: web.AppRunner = web.AppRunner(self.webapp)
        self._site: Optional[web.TCPSite] = None

    async def load(self) -> None:
        """Register REST API Calls."""
        self._register_addons()
        self._register_audio()
        self._register_auth()
        self._register_cli()
        self._register_discovery()
        self._register_dns()
        self._register_docker()
        self._register_hardware()
        self._register_homeassistant()
        self._register_host()
        self._register_info()
        self._register_ingress()
        self._register_multicast()
        self._register_network()
        self._register_observer()
        self._register_os()
        self._register_jobs()
        self._register_panel()
        self._register_proxy()
        self._register_resolution()
        self._register_services()
        self._register_snapshots()
        self._register_supervisor()
        self._register_store()
        self._register_security()

        await self.start()

    def _register_host(self) -> None:
        """Register hostcontrol functions."""
        api_host = APIHost()
        api_host.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/host/info", api_host.info),
                web.get("/host/logs", api_host.logs),
                web.post("/host/reboot", api_host.reboot),
                web.post("/host/shutdown", api_host.shutdown),
                web.post("/host/reload", api_host.reload),
                web.post("/host/options", api_host.options),
                web.get("/host/services", api_host.services),
                web.post("/host/services/{service}/stop", api_host.service_stop),
                web.post("/host/services/{service}/start", api_host.service_start),
                web.post("/host/services/{service}/restart", api_host.service_restart),
                web.post("/host/services/{service}/reload", api_host.service_reload),
            ]
        )

    def _register_network(self) -> None:
        """Register network functions."""
        api_network = APINetwork()
        api_network.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/network/info", api_network.info),
                web.post("/network/reload", api_network.reload),
                web.get(
                    "/network/interface/{interface}/info", api_network.interface_info
                ),
                web.post(
                    "/network/interface/{interface}/update",
                    api_network.interface_update,
                ),
                web.get(
                    "/network/interface/{interface}/accesspoints",
                    api_network.scan_accesspoints,
                ),
                web.post(
                    "/network/interface/{interface}/vlan/{vlan}",
                    api_network.create_vlan,
                ),
            ]
        )

    def _register_os(self) -> None:
        """Register OS functions."""
        api_os = APIOS()
        api_os.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/os/info", api_os.info),
                web.post("/os/update", api_os.update),
                web.post("/os/config/sync", api_os.config_sync),
            ]
        )

    def _register_security(self) -> None:
        """Register Security functions."""
        api_security = APISecurity()
        api_security.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/security/info", api_security.info),
                web.post("/security/options", api_security.options),
            ]
        )

    def _register_jobs(self) -> None:
        """Register Jobs functions."""
        api_jobs = APIJobs()
        api_jobs.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/jobs/info", api_jobs.info),
                web.post("/jobs/options", api_jobs.options),
                web.post("/jobs/reset", api_jobs.reset),
            ]
        )

    def _register_cli(self) -> None:
        """Register HA cli functions."""
        api_cli = APICli()
        api_cli.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/cli/info", api_cli.info),
                web.get("/cli/stats", api_cli.stats),
                web.post("/cli/update", api_cli.update),
            ]
        )

    def _register_observer(self) -> None:
        """Register Observer functions."""
        api_observer = APIObserver()
        api_observer.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/observer/info", api_observer.info),
                web.get("/observer/stats", api_observer.stats),
                web.post("/observer/update", api_observer.update),
            ]
        )

    def _register_multicast(self) -> None:
        """Register Multicast functions."""
        api_multicast = APIMulticast()
        api_multicast.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/multicast/info", api_multicast.info),
                web.get("/multicast/stats", api_multicast.stats),
                web.get("/multicast/logs", api_multicast.logs),
                web.post("/multicast/update", api_multicast.update),
                web.post("/multicast/restart", api_multicast.restart),
            ]
        )

    def _register_hardware(self) -> None:
        """Register hardware functions."""
        api_hardware = APIHardware()
        api_hardware.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/hardware/info", api_hardware.info),
                web.get("/hardware/audio", api_hardware.audio),
            ]
        )

    def _register_info(self) -> None:
        """Register info functions."""
        api_info = APIInfo()
        api_info.coresys = self.coresys

        self.webapp.add_routes([web.get("/info", api_info.info)])

    def _register_resolution(self) -> None:
        """Register info functions."""
        api_resolution = APIResoulution()
        api_resolution.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/resolution/info", api_resolution.info),
                web.post(
                    "/resolution/check/{check}/options", api_resolution.options_check
                ),
                web.post("/resolution/check/{check}/run", api_resolution.run_check),
                web.post(
                    "/resolution/suggestion/{suggestion}",
                    api_resolution.apply_suggestion,
                ),
                web.delete(
                    "/resolution/suggestion/{suggestion}",
                    api_resolution.dismiss_suggestion,
                ),
                web.delete(
                    "/resolution/issue/{issue}",
                    api_resolution.dismiss_issue,
                ),
                web.post("/resolution/healthcheck", api_resolution.healthcheck),
            ]
        )

    def _register_auth(self) -> None:
        """Register auth functions."""
        api_auth = APIAuth()
        api_auth.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/auth", api_auth.auth),
                web.post("/auth", api_auth.auth),
                web.post("/auth/reset", api_auth.reset),
                web.delete("/auth/cache", api_auth.cache),
            ]
        )

    def _register_supervisor(self) -> None:
        """Register Supervisor functions."""
        api_supervisor = APISupervisor()
        api_supervisor.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/supervisor/ping", api_supervisor.ping),
                web.get("/supervisor/info", api_supervisor.info),
                web.get("/supervisor/stats", api_supervisor.stats),
                web.get("/supervisor/logs", api_supervisor.logs),
                web.post("/supervisor/update", api_supervisor.update),
                web.post("/supervisor/reload", api_supervisor.reload),
                web.post("/supervisor/restart", api_supervisor.restart),
                web.post("/supervisor/options", api_supervisor.options),
                web.post("/supervisor/repair", api_supervisor.repair),
            ]
        )

    def _register_homeassistant(self) -> None:
        """Register Home Assistant functions."""
        api_hass = APIHomeAssistant()
        api_hass.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/core/info", api_hass.info),
                web.get("/core/logs", api_hass.logs),
                web.get("/core/stats", api_hass.stats),
                web.post("/core/options", api_hass.options),
                web.post("/core/update", api_hass.update),
                web.post("/core/restart", api_hass.restart),
                web.post("/core/stop", api_hass.stop),
                web.post("/core/start", api_hass.start),
                web.post("/core/check", api_hass.check),
                web.post("/core/rebuild", api_hass.rebuild),
                # Remove with old Supervisor fallback
                web.get("/homeassistant/info", api_hass.info),
                web.get("/homeassistant/logs", api_hass.logs),
                web.get("/homeassistant/stats", api_hass.stats),
                web.post("/homeassistant/options", api_hass.options),
                web.post("/homeassistant/update", api_hass.update),
                web.post("/homeassistant/restart", api_hass.restart),
                web.post("/homeassistant/stop", api_hass.stop),
                web.post("/homeassistant/start", api_hass.start),
                web.post("/homeassistant/check", api_hass.check),
                web.post("/homeassistant/rebuild", api_hass.rebuild),
            ]
        )

    def _register_proxy(self) -> None:
        """Register Home Assistant API Proxy."""
        api_proxy = APIProxy()
        api_proxy.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/core/api/websocket", api_proxy.websocket),
                web.get("/core/websocket", api_proxy.websocket),
                web.get("/core/api/stream", api_proxy.stream),
                web.post("/core/api/{path:.+}", api_proxy.api),
                web.get("/core/api/{path:.+}", api_proxy.api),
                web.get("/core/api/", api_proxy.api),
                # Remove with old Supervisor fallback
                web.get("/homeassistant/api/websocket", api_proxy.websocket),
                web.get("/homeassistant/websocket", api_proxy.websocket),
                web.get("/homeassistant/api/stream", api_proxy.stream),
                web.post("/homeassistant/api/{path:.+}", api_proxy.api),
                web.get("/homeassistant/api/{path:.+}", api_proxy.api),
                web.get("/homeassistant/api/", api_proxy.api),
            ]
        )

    def _register_addons(self) -> None:
        """Register Add-on functions."""
        api_addons = APIAddons()
        api_addons.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/addons", api_addons.list),
                web.post("/addons/reload", api_addons.reload),
                web.get("/addons/{addon}/info", api_addons.info),
                web.post("/addons/{addon}/uninstall", api_addons.uninstall),
                web.post("/addons/{addon}/start", api_addons.start),
                web.post("/addons/{addon}/stop", api_addons.stop),
                web.post("/addons/{addon}/restart", api_addons.restart),
                web.post("/addons/{addon}/options", api_addons.options),
                web.post(
                    "/addons/{addon}/options/validate", api_addons.options_validate
                ),
                web.get("/addons/{addon}/options/config", api_addons.options_config),
                web.post("/addons/{addon}/rebuild", api_addons.rebuild),
                web.get("/addons/{addon}/logs", api_addons.logs),
                web.get("/addons/{addon}/icon", api_addons.icon),
                web.get("/addons/{addon}/logo", api_addons.logo),
                web.get("/addons/{addon}/changelog", api_addons.changelog),
                web.get("/addons/{addon}/documentation", api_addons.documentation),
                web.post("/addons/{addon}/stdin", api_addons.stdin),
                web.post("/addons/{addon}/security", api_addons.security),
                web.get("/addons/{addon}/stats", api_addons.stats),
            ]
        )

    def _register_ingress(self) -> None:
        """Register Ingress functions."""
        api_ingress = APIIngress()
        api_ingress.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.post("/ingress/session", api_ingress.create_session),
                web.post("/ingress/validate_session", api_ingress.validate_session),
                web.get("/ingress/panels", api_ingress.panels),
                web.view("/ingress/{token}/{path:.*}", api_ingress.handler),
            ]
        )

    def _register_snapshots(self) -> None:
        """Register snapshots functions."""
        api_snapshots = APISnapshots()
        api_snapshots.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/snapshots", api_snapshots.list),
                web.post("/snapshots/reload", api_snapshots.reload),
                web.post("/snapshots/new/full", api_snapshots.snapshot_full),
                web.post("/snapshots/new/partial", api_snapshots.snapshot_partial),
                web.post("/snapshots/new/upload", api_snapshots.upload),
                web.get("/snapshots/{snapshot}/info", api_snapshots.info),
                web.delete("/snapshots/{snapshot}", api_snapshots.remove),
                web.post(
                    "/snapshots/{snapshot}/restore/full", api_snapshots.restore_full
                ),
                web.post(
                    "/snapshots/{snapshot}/restore/partial",
                    api_snapshots.restore_partial,
                ),
                web.get("/snapshots/{snapshot}/download", api_snapshots.download),
                # Old, remove at end of 2020
                web.post("/snapshots/{snapshot}/remove", api_snapshots.remove),
            ]
        )

    def _register_services(self) -> None:
        """Register services functions."""
        api_services = APIServices()
        api_services.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/services", api_services.list),
                web.get("/services/{service}", api_services.get_service),
                web.post("/services/{service}", api_services.set_service),
                web.delete("/services/{service}", api_services.del_service),
            ]
        )

    def _register_discovery(self) -> None:
        """Register discovery functions."""
        api_discovery = APIDiscovery()
        api_discovery.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/discovery", api_discovery.list),
                web.get("/discovery/{uuid}", api_discovery.get_discovery),
                web.delete("/discovery/{uuid}", api_discovery.del_discovery),
                web.post("/discovery", api_discovery.set_discovery),
            ]
        )

    def _register_dns(self) -> None:
        """Register DNS functions."""
        api_dns = APICoreDNS()
        api_dns.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/dns/info", api_dns.info),
                web.get("/dns/stats", api_dns.stats),
                web.get("/dns/logs", api_dns.logs),
                web.post("/dns/update", api_dns.update),
                web.post("/dns/options", api_dns.options),
                web.post("/dns/restart", api_dns.restart),
                web.post("/dns/reset", api_dns.reset),
            ]
        )

    def _register_audio(self) -> None:
        """Register Audio functions."""
        api_audio = APIAudio()
        api_audio.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/audio/info", api_audio.info),
                web.get("/audio/stats", api_audio.stats),
                web.get("/audio/logs", api_audio.logs),
                web.post("/audio/update", api_audio.update),
                web.post("/audio/restart", api_audio.restart),
                web.post("/audio/reload", api_audio.reload),
                web.post("/audio/profile", api_audio.set_profile),
                web.post("/audio/volume/{source}/application", api_audio.set_volume),
                web.post("/audio/volume/{source}", api_audio.set_volume),
                web.post("/audio/mute/{source}/application", api_audio.set_mute),
                web.post("/audio/mute/{source}", api_audio.set_mute),
                web.post("/audio/default/{source}", api_audio.set_default),
            ]
        )

    def _register_store(self) -> None:
        """Register store endpoints."""
        api_store = APIStore()
        api_store.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/store", api_store.store_info),
                web.get("/store/addons", api_store.addons_list),
                web.get("/store/addons/{addon}", api_store.addons_addon_info),
                web.get("/store/addons/{addon}/{version}", api_store.addons_addon_info),
                web.post(
                    "/store/addons/{addon}/install", api_store.addons_addon_install
                ),
                web.post(
                    "/store/addons/{addon}/install/{version}",
                    api_store.addons_addon_install,
                ),
                web.post("/store/addons/{addon}/update", api_store.addons_addon_update),
                web.post(
                    "/store/addons/{addon}/update/{version}",
                    api_store.addons_addon_update,
                ),
                web.post("/store/reload", api_store.reload),
                web.get("/store/repositories", api_store.repositories_list),
                web.get(
                    "/store/repositories/{repository}",
                    api_store.repositories_repository_info,
                ),
            ]
        )

        # Reroute from legacy
        self.webapp.add_routes(
            [
                web.post("/addons/{addon}/install", api_store.addons_addon_install),
                web.post("/addons/{addon}/update", api_store.addons_addon_update),
            ]
        )

    def _register_panel(self) -> None:
        """Register panel for Home Assistant."""
        panel_dir = Path(__file__).parent.joinpath("panel")
        self.webapp.add_routes([web.static("/app", panel_dir)])

    def _register_docker(self) -> None:
        """Register docker configuration functions."""
        api_docker = APIDocker()
        api_docker.coresys = self.coresys

        self.webapp.add_routes(
            [
                web.get("/docker/info", api_docker.info),
                web.get("/docker/registries", api_docker.registries),
                web.post("/docker/registries", api_docker.create_registry),
                web.delete("/docker/registries/{hostname}", api_docker.remove_registry),
            ]
        )

    async def start(self) -> None:
        """Run RESTful API webserver."""
        await self._runner.setup()
        self._site = web.TCPSite(
            self._runner, host="0.0.0.0", port=80, shutdown_timeout=5
        )

        try:
            await self._site.start()
        except OSError as err:
            _LOGGER.critical("Failed to create HTTP server at 0.0.0.0:80 -> %s", err)
        else:
            _LOGGER.info("Starting API on %s", self.sys_docker.network.supervisor)

    async def stop(self) -> None:
        """Stop RESTful API webserver."""
        if not self._site:
            return

        # Shutdown running API
        await self._site.stop()
        await self._runner.cleanup()

        _LOGGER.info("Stopping API on %s", self.sys_docker.network.supervisor)
