#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Dépend de websites_test_framework.
"""

import re
from typing import List

from websites_test_framework import WebsiteTest, run_and_collect, test
from websites_test_framework.custom_types import TestReturn
from websites_test_framework.tools import warn


WEBSITES_PATH = "/home/nicolas/Travail/enseignements/S1/SAE/SAE105/rendus/*/www/S105"
OUTPUT_DIR = "/home/nicolas/Travail/enseignements/S1/SAE/SAE105"
PATH_ON_SERVER = "/S105"  # to rewrite absolute URLS. Server root is www/, not www/S105/


class S105WebsiteTest(WebsiteTest):
    def get_authors(self):
        try:
            file_content = (self.path / "auteurs.txt").read_text("utf8")
        except FileNotFoundError:
            warn("Pas de fichier auteurs.txt", str(self.path))
            file_content = ""
        return [author for line in file_content.split("\n") if (author := line.strip())]

    # Set weight 0 to .test_broken_url_in_html().
    @test(title="Broken URL", weight=0)
    def test_broken_url_in_html(self) -> TestReturn:
        return super().test_broken_url_in_html()

    @test(title="auteurs.txt", weight=0.5)
    def test_authors_file_conformity(self) -> TestReturn:
        try:
            file_content = (self.path / "auteurs.txt").read_text("utf8")
        except FileNotFoundError:
            return 0, ["Pas de fichier auteurs.txt"]
        if any(re.search(r"\d", line) for line in file_content.split("\n")):
            return 0, ["Contenu incorrect (chiffre)"]
        return 1, []

    @test(title="1 seule page", weight=0.5)
    def test_une_seule_page(self) -> TestReturn:
        if len(self.website.html_files) != 1:
            return 0, ["Plusieurs pages HTML"]
        elif self.website.html_files[0].path.name != "index.html":
            return 0.25, ["Nom incorrect"]
        return 1, []

    @test(title="input types", weight=0.5, relative=True)
    def test_input_types_diversity(self) -> TestReturn:
        if len(self.website.html_files) != 1:
            return 0, ["Plusieurs pages HTML"]
        input_tags = self.website.html_files[0].structure.find_all("input")
        return len({tag.get("type") for tag in input_tags}), []

    @test(title="input attributes", weight=0.5, relative=True)
    def test_input_attrs_diversity(self) -> TestReturn:
        if len(self.website.html_files) != 1:
            return 0, ["Plusieurs pages HTML"]
        input_tags = self.website.html_files[0].structure.find_all("input")
        attrs = set()
        for tag in input_tags:
            attrs |= set(tag.attrs)
        return len(attrs), []

    @test(title="specific attributes", weight=0.5, relative=True)
    def test_for_specific_attributes(self) -> TestReturn:
        if len(self.website.html_files) != 1:
            return 0, ["Plusieurs pages HTML"]
        input_tags = self.website.html_files[0].structure.find_all("input")
        attrs = set()
        for tag in input_tags:
            attrs |= set(tag.attrs)
        score = (
            2 * ("pattern" in attrs)
            + 2 * ("placeholder" in attrs)
            + 4 * ("required" in attrs)
            + ("min" in attrs)
            + ("max" in attrs)
        )
        return score / 10, []

    @test(title="form pseudo-elts", weight=1)
    def test_form_pseudo_elements(self) -> TestReturn:
        if len(self.website.html_files) != 1:
            return 0, ["Plusieurs pages HTML"]
        score = 0
        for pseudo_elt in (":valide", ":invalid", ":required", ":optional"):
            if any((pseudo_elt in selector) for selector in self.website.all_selectors):
                score += 1
        return score ** (1 / 2) / 2, []

    @test(title="form structure", weight=1)
    def test_form(self) -> TestReturn:
        score = 0
        log = []
        try:
            html = self.website.html_files[0].structure.html
            forms = html.find_all("form")
            if forms:
                score += 1
            else:
                log.append("No <form> !")
            if len(forms) == 1:
                score += 1
            else:
                log.append("There should be only one <form> !")
            if html.form.fieldset is not None:
                score += 1
            if html.form.input is not None:
                score += 1
            if html.form.label is not None:
                score += 1
            if html.form.find("input", type="submit") or html.form.find("button"):
                score += 1
        except (AttributeError, IndexError):
            pass
        return score / 6, log

    @test(title="Labels", weight=0.5)
    def test_label_linked_to_input(self) -> TestReturn:
        score: float = 0
        log: List[str] = []
        try:
            html = self.website.html_files[0].structure.html
            good = 0
            total = 0
            for label in html.form.find_all("label"):
                total += 1
                # Label may be linked implicitly or explicitly to input fields.
                # https://www.w3.org/TR/html401/interact/forms.html#h-17.9.1
                for_ = label.attrs.get("for")
                if len(html.form.find_all("input", id=for_)) == 1:
                    # explicitly: using `for` attribute.
                    good += 1
                elif label.input is not None:
                    # implicitly: input is inside label.
                    good += 1
            if total != 0:
                score = good / total
        except (AttributeError, IndexError):
            pass
        return score, log


def main(path=WEBSITES_PATH, output=OUTPUT_DIR, path_on_server=PATH_ON_SERVER):
    run_and_collect(S105WebsiteTest, path, output, path_on_server)


if __name__ == "__main__":
    main()
