# pyright: reportAttributeAccessIssue=false

import sys
from html.parser import HTMLParser

import requests
from httpretty import HTTPretty
from openid import oidutil

from ...backends.utils import load_backends
from ...utils import module_member, parse_qs
from ..models import TestAssociation, TestNonce, TestStorage, TestUserSocialAuth, User
from ..strategy import TestStrategy
from .base import BaseBackendTest

sys.path.insert(0, "..")


# Patch to remove the too-verbose output until a new version is released
oidutil.log = lambda *args, **kwargs: None


class FormHTMLParser(HTMLParser):
    form = {}
    inputs = {}

    def handle_starttag(self, tag, attrs):
        attrs = dict(attrs)
        if tag == "form":
            self.form.update(attrs)
        elif tag == "input" and "name" in attrs:
            self.inputs[attrs["name"]] = attrs["value"]


class OpenIdTest(BaseBackendTest):
    backend_path = None
    backend = None
    access_token_body = None
    user_data_body = None
    user_data_url = ""
    expected_username = ""
    settings = None
    partial_login_settings = None
    raw_complete_url = "/complete/{0}/"

    def setUp(self):
        HTTPretty.enable(allow_net_connect=False)
        Backend = module_member(self.backend_path)
        self.strategy = TestStrategy(TestStorage)
        self.complete_url = self.raw_complete_url.format(Backend.name)
        self.backend = Backend(self.strategy, redirect_uri=self.complete_url)
        self.strategy.set_settings(
            {
                "SOCIAL_AUTH_AUTHENTICATION_BACKENDS": (
                    self.backend_path,
                    "social_core.tests.backends.test_broken.BrokenBackendAuth",
                )
            }
        )
        # Force backends loading to trash PSA cache
        load_backends(
            self.strategy.get_setting("SOCIAL_AUTH_AUTHENTICATION_BACKENDS"),
            force_load=True,
        )

    def tearDown(self):
        self.strategy = None
        User.reset_cache()
        TestUserSocialAuth.reset_cache()
        TestNonce.reset_cache()
        TestAssociation.reset_cache()
        HTTPretty.disable()
        HTTPretty.reset()

    def get_form_data(self, html):
        parser = FormHTMLParser()
        parser.feed(html)
        return parser.form, parser.inputs

    def openid_url(self):
        return self.backend.openid_url()

    def post_start(self):
        pass

    def do_start(self):
        HTTPretty.register_uri(
            HTTPretty.GET,
            self.openid_url(),
            status=200,
            body=self.discovery_body,
            content_type="application/xrds+xml",
        )
        start = self.backend.start()
        self.post_start()
        form, inputs = self.get_form_data(start)
        action = form.get("action")
        assert action, "The form action must be set in the test"
        HTTPretty.register_uri(
            HTTPretty.POST, action, status=200, body=self.server_response
        )
        response = requests.post(action, data=inputs)
        self.strategy.set_request_data(parse_qs(response.content), self.backend)
        HTTPretty.register_uri(
            HTTPretty.POST, form.get("action"), status=200, body="is_valid:true\n"
        )
        return self.backend.complete()
