"""Client for interacting with an Octoprint server""" 

import asyncio
import logging
from typing import Optional

import aiohttp

from .api import OctoprintApi
from .const import (CONNECTION_COMMAND_CONNECT, CONNECTION_COMMAND_DISCONNECT,
                    JOB_COMMAND_CANCEL, JOB_COMMAND_PAUSE,
                    JOB_COMMAND_PAUSE_PAUSE, JOB_COMMAND_PAUSE_RESUME)
from .exceptions import OctoprintException
from .job import OctoprintJobInfo
from .printer import OctoprintPrinterInfo
from .server import OctoprintServerInfo
from .settings import DiscoverySettings, TrackingSetting, WebcamSettings

_LOGGER = logging.getLogger(__name__)

class OctoprintClient:
    """Client for interacting with an Octoprint server"""
    def __init__(self, host: str, session: aiohttp.ClientSession = None, port: int = 80, ssl: bool = False, path: str = "/"):
        protocol = "https" if ssl else "http"
        self._base_url = f"{protocol}://{host}:{port}{path}"
        if session is None:
            connector = aiohttp.TCPConnector(force_close=True)
            session = aiohttp.ClientSession(connector=connector)
        self._api = OctoprintApi(self._base_url, session)

    async def request_app_key(self, app_name: str, user:str, timeout:int = 5) -> str:
        if not await self._api.check_appkeys_enabled():
            raise OctoprintException("The application keys plugin appears to be disabled")

        request_id = await self._api.appkeys_start_auth(app_name, user)
        status_response = await self._api.appkeys_check_status(request_id)
        while ("api_key" not in status_response):
            await asyncio.sleep(1)
            status_response = await self._api.appkeys_check_status(request_id)
            if "message" in status_response:
                _LOGGER.debug("Message: %s", status_response["message"])
                
            timeout -= 1
            if timeout <= 0:
                raise OctoprintException("Timeout waiting for authorization")

        return status_response["api_key"]
    
    def set_api_key(self, api_key: str) -> None:
        self._api.set_api_key(api_key)

    async def get_printer_info(self) -> OctoprintPrinterInfo:
        response = await self._api.get_printer_info()
        return OctoprintPrinterInfo(response)

    async def get_job_info(self) -> OctoprintJobInfo:
        response = await self._api.get_job_info()
        return OctoprintJobInfo(response)

    async def get_server_info(self) -> OctoprintServerInfo:
        response = await self._api.get_server_info()
        return OctoprintServerInfo(response)

    async def get_tracking_info(self) -> Optional[TrackingSetting]:
        response = await self._api.get_settings()
        if "tracking" in response["plugins"]:
            return TrackingSetting(response["plugins"]["tracking"])
        
        return None

    async def get_webcam_info(self) -> Optional[WebcamSettings]:
        response = await self._api.get_settings()
        if "webcam" in response:
            return WebcamSettings(self._base_url, response["webcam"])
        
        return None

    async def get_discovery_info(self) -> Optional[DiscoverySettings]:
        response = await self._api.get_settings()
        if "discovery" in response["plugins"]:
            return DiscoverySettings(response["plugins"]["discovery"])

        return None

    async def shutdown(self) -> None:
        _LOGGER.debug("Sending shutdown command")
        await self._api.issue_system_command("core", "shutdown")

    async def reboot_system(self) -> None:
        _LOGGER.debug("Sending reboot command")
        await self._api.issue_system_command("core", "reboot")

    async def restart(self) -> None:
        _LOGGER.debug("Sending restart command")
        await self._api.issue_system_command("core", "restart")

    async def cancel_job(self) -> None:
        _LOGGER.debug("Sending cancel job command")
        await self._api.issue_job_command(JOB_COMMAND_CANCEL)

    async def pause_job(self) -> None:
        _LOGGER.debug("Sending pause job command")
        await self._api.issue_job_command(JOB_COMMAND_PAUSE, JOB_COMMAND_PAUSE_PAUSE)

    async def resume_job(self) -> None:
        _LOGGER.debug("Sending resume job command")
        await self._api.issue_job_command(JOB_COMMAND_PAUSE, JOB_COMMAND_PAUSE_RESUME)

    async def disconnect(self) -> None:
        _LOGGER.debug("Disconnecting from printer")
        await self._api.issue_connection_command(CONNECTION_COMMAND_DISCONNECT)

    async def connect(self, port: Optional[str] = None, baud_rate: Optional[int] = None, printer_profile: Optional[str] = None, save: Optional[bool] = None, auto_connect: Optional[bool] = None) -> None:
        _LOGGER.debug("Connecting to printer")
        await self._api.issue_connection_command(CONNECTION_COMMAND_CONNECT, port=port, baudrate=baud_rate, printerprofile=printer_profile, save=save, autoconnect=auto_connect)

    async def set_bed_temperature(self, target: int, offset: int = 0) -> None:
        _LOGGER.debug("Setting bed temp to %d, offset %d", target, offset)
        await self._api.set_bed_temperature(target, offset=offset)

    async def turn_bed_off(self) -> None:
        _LOGGER.debug("Turning off bed")
        await self._api.set_bed_temperature(0)

    async def set_tool_temperature(self, tool: str, target: int) -> None:
        _LOGGER.debug("Setting tool %s temp to %d", tool, target)
        targets = {
            tool: target
        }
        await self._api.issue_tool_command("target", targets=targets)

    async def turn_tool_off(self, tool: str) -> None:
        _LOGGER.debug("Turning off tool %s", tool)
        targets = {
            tool: 0
        }
        await self._api.issue_tool_command("target", targets=targets)
