"""Python Package for controlling Alexa devices (echo dot, etc) programmatically.

SPDX-License-Identifier: Apache-2.0

This provides a login by proxy method.
Built on https://github.com/alandtse/auth_capture_proxy

For more details about this api, please refer to the documentation at
https://gitlab.com/keatontaylor/alexapy
"""

import logging
from functools import partial

import authcaptureproxy
from bs4 import BeautifulSoup
from yarl import URL

from alexapy.alexalogin import AlexaLogin
from alexapy.helpers import hide_email, hide_password

_LOGGER = logging.getLogger(__name__)


class AlexaProxy(authcaptureproxy.AuthCaptureProxy):
    """Class to handle proxy login connections to Alexa."""

    def __init__(self, login: AlexaLogin, base_url: str) -> None:
        """Initialize proxy object.

        Args:
            login (AlexaLogin): AlexaLogin object to update after completion of proxy.
            base_url (Text): base url for proxy location. e.g., http://192.168.1.1

        """
        super().__init__(URL(base_url), URL(login.start_url))
        self._login: AlexaLogin = login
        self._config_flow_id = None
        self._callback_url = None
        self.tests = {"test_amazon_url": self.test_amazon_url}
        self.modifiers = {
            "autofill": partial(
                self.autofill,
                {
                    "email": self._login.email,
                    "password": self._login.password,
                    "otpCode": self._login.get_totp_token(),
                },
            )
        }

    async def test_amazon_url(self, resp, data, query) -> URL | str | None:
        """Test for Alexa success.

        Args
            resp (httpx.Response): The aiohttp response.
            data (Dict[Text, Any]): Dictionary of all post data captured through proxy with overwrites for duplicate keys.
            query (Dict[Text, Any]): Dictionary of all query data with overwrites for duplicate keys.

        Returns
            Optional[Union[URL, Text]]: URL for a http 302 redirect or Text to display on success. None indicates test did not pass.

        """  # noqa: E501
        if URL(str(resp.url)).path in ["/ap/maplanding", "/spa/index.html"]:
            self._login.session.cookie_jar.update_cookies(self.session.cookies)
            self._login.authorization_code = URL(str(resp.url)).query.get(
                "openid.oa2.authorization_code"
            )
            self._login.access_token = URL(str(resp.url)).query.get(
                "openid.oa2.access_token"
            )
            self._config_flow_id = self.init_query.get("config_flow_id")
            self._callback_url = self.init_query.get("callback_url")
            # Reset so proxy can be reused
            # Unfortunately because the route has a handler bound and cannot be changed, we need to remove the modifiers for other proxies. # noqa: E501
            await self.reset_data()
            if self._callback_url:
                return URL(self._callback_url)
            return f"Successfully logged in as {self._login.email} for flow {self._config_flow_id}. Please close the window."  # noqa: E501
        if URL(str(resp.url)).path in [
            "/"
        ]:  # sometimes the redirect after the captcha fails, redirect manually
            return self._login.proxy_url

    def change_login(self, login: AlexaLogin) -> None:
        """Change login.

        Args:
            login (AlexaLogin): AlexaLogin object to update after completion of proxy.
        """
        self._login = login
        self.tests = {"test_amazon_url": self.test_amazon_url}
        self.modifiers = {
            "autofill": partial(
                self.autofill,
                {
                    "email": self._login.email,
                    "password": self._login.password,
                    "otpCode": self._login.get_totp_token(),
                },
            )
        }

    def autofill(self, items: dict, html: str) -> str:
        """Autofill input tags in form in html.

        Args
            html (Text): html to convert
            items (dict): Dictionary of values to fill

        Returns
            Text: html with values filled in

        """
        soup: BeautifulSoup = BeautifulSoup(html, "html.parser")
        for item, value in items.items():
            for html_tag in soup.find_all(attrs={"name": item}):
                if not html_tag.get("value"):
                    html_tag["value"] = value
                    if self._login._debug:
                        _LOGGER.debug(
                            "Filled %s",
                            str(html_tag).replace(
                                value,
                                (
                                    hide_password(value)
                                    if item == "password"
                                    else hide_email(value)
                                    if item == "email"
                                    else value
                                ),
                            ),
                        )
        return str(soup)
