"""Implementation of legacy pairing for AirPlay."""

import binascii
import logging
import plistlib
from typing import Dict, Optional, Tuple

from pyatv import exceptions
from pyatv.auth.hap_pairing import (
    HapCredentials,
    PairSetupProcedure,
    PairVerifyProcedure,
)
from pyatv.protocols.airplay.srp import LegacySRPAuthHandler
from pyatv.support.http import HttpConnection, HttpResponse, decode_bplist_from_body

_LOGGER = logging.getLogger(__name__)

_AIRPLAY_HEADERS = {
    "User-Agent": "AirPlay/320.20",
    "Connection": "keep-alive",
}


class AirPlayLegacyPairSetupProcedure(PairSetupProcedure):
    """Authenticate a device for AirPlay playback."""

    def __init__(
        self, http: HttpConnection, auth_handler: LegacySRPAuthHandler
    ) -> None:
        """Initialize a new AirPlayLegacyPairSetupProcedure."""
        self.http = http
        self.srp = auth_handler

    async def start_pairing(self) -> None:
        """Start the pairing process.

        This method will show the expected PIN on screen.
        """
        await self.http.post("/pair-pin-start", headers=_AIRPLAY_HEADERS)

    async def finish_pairing(
        self, username: str, pin_code: int, _: Optional[str]
    ) -> HapCredentials:
        """Finish pairing process.

        A username (generated by new_credentials) and the PIN code shown on
        screen must be provided.
        """
        # Step 1
        client_id = (
            binascii.hexlify(self.srp.credentials.client_id).decode("ascii").upper()
        )
        self.srp.step1(client_id, pin_code)
        resp = await self._send_plist(method="pin", user=client_id)
        body = decode_bplist_from_body(resp)
        if not isinstance(body, dict):
            raise exceptions.ProtocolError(f"exoected dict, got {type(body).__name__}")

        # Step 2
        pub_key, key_proof = self.srp.step2(body["pk"], body["salt"])
        await self._send_plist(
            pk=binascii.unhexlify(pub_key), proof=binascii.unhexlify(key_proof)
        )

        # Step 3
        epk, tag = self.srp.step3()
        await self._send_plist(epk=epk, authTag=tag)
        return self.srp.credentials

    async def _send_plist(self, **kwargs) -> HttpResponse:
        plist: Dict[str, str] = dict((str(k), v) for k, v in kwargs.items())

        headers = _AIRPLAY_HEADERS.copy()
        headers["Content-Type"] = "application/x-apple-binary-plist"

        # TODO: For some reason pylint does not find FMT_BINARY, why?
        # pylint: disable=no-member
        return await self.http.post(
            "/pair-setup-pin", body=plistlib.dumps(plist, fmt=plistlib.FMT_BINARY)
        )


class AirPlayLegacyPairVerifyProcedure(PairVerifyProcedure):
    """Verify if a device is allowed to perform AirPlay playback."""

    def __init__(
        self, http: HttpConnection, auth_handler: LegacySRPAuthHandler
    ) -> None:
        """Initialize a new AirPlayPairingVerifier."""
        self.http = http
        self.srp = auth_handler

    async def verify_credentials(self) -> bool:
        """Verify if device is allowed to use AirPlau."""
        resp = await self._send(self.srp.verify1())

        atv_public_secret = resp.body[0:32]
        data = resp.body[32:]  # TODO: what is this?
        await self._send(self.srp.verify2(atv_public_secret, data))
        return False

    async def _send(self, data: bytes) -> HttpResponse:
        headers = _AIRPLAY_HEADERS.copy()
        headers["Content-Type"] = "application/octet-stream"
        return await self.http.post("/pair-verify", headers=headers, body=data)

    def encryption_keys(
        self, salt: str, output_info: str, input_info: str
    ) -> Tuple[bytes, bytes]:
        """Return derived encryption keys."""
        raise exceptions.NotSupportedError(
            "encryption keys not supported by legacy auth"
        )
