# Menu.py

import pickle
import re
from collections.abc import Callable
from functools import wraps
from pathlib import Path
from typing import ParamSpec

import questionary
from rich.table import Column, Table
from rich.traceback import install
from simple_text_ui import TextUI
from website import Domain, Google, Person, Website, console, err_console

install(console=err_console, show_locals=True)

STORAGE_FILE: Path = Path("websites.pkl")

P = ParamSpec("P")


def cleanup(func: Callable) -> Callable:
    @wraps(func)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> object:
        try:
            return func(*args, **kwargs)
        finally:
            input("Press enter when done. ")
            console.clear()

    return wrapper


class WebsiteMenu:
    def __init__(self) -> None:
        com: Domain = Domain("com")
        org: Domain = Domain("org")
        io: Domain = Domain("io")
        edu: Domain = Domain("edu")
        diamonds: Domain = Domain("diamonds")
        orequeen: Domain = Domain("orequeen")

        self.google: Google = Google(com, org, io, edu, diamonds, orequeen)

        site: Website = Website(
            "Misha's website for only Misha because he is the best!"
        )
        self.misha: Person = Person("Misha")
        self.misha.set_website(site)
        self.misha.publish(domain=diamonds)

        site = Website(
            "[blue]Mama likes Catan. Do you?\nI hope so because:\nC\nA\nT\nA\nN\n!"
        )
        self.mama: Person = Person("Mama")
        self.mama.set_website(site)
        self.mama.publish(domain=orequeen)

    @cleanup
    def search(self) -> None:
        "Search the Internet"
        self.google.search(questionary.text("What site would you like to visit?").ask())

    @cleanup
    def add_website(self) -> None:
        "Add a website"
        valid = False
        while not valid:
            try:
                name, domain = self.parse_url(
                    questionary.text(
                        "What is the URL for your site? (eg. misha.diamonds)"
                    ).ask()
                )
            except ValueError as exc:
                err_console.print(f"URL Invalid: {exc}")
            else:
                valid = True
        text = self.format_text(
            questionary.text("What is the content of your site?").ask()
        )
        site: Website = Website(text)
        try:
            site.publish(name, self.get_domain(domain)) # type: ignore
        except ValueError as exc:
            err_console.print(f"Failed to create website: {exc}")
        else:
            console.print(f"Website {site.url} created", style="info")

    @cleanup
    def display_websites(self) -> None:
        "List all websites"
        table = Table(Column("Websites"), show_lines=True)
        for domain in self.google.domains.values():
            for website in domain.websites.values():
                table.add_row(website.url)
        console.print(table)

    @cleanup
    def save(self) -> None:
        "Save to a file"
        ans: bool = questionary.confirm(
            "Are you really sure you want to save? This will erase all currently saved data."
        ).ask()
        if ans:
            with STORAGE_FILE.open("wb") as file:
                pickle.dump(self.google, file, protocol=pickle.HIGHEST_PROTOCOL)
            console.print("Save succeeded.", style="info")
        else:
            console.print("Save terminated.", style="info")

    @cleanup
    def load(self) -> None:
        "Load saved data"
        ans: bool = questionary.confirm(
            "Are you sure you want to load saved data? This will erase all data currently in the program."
        ).ask()
        if ans:
            if not STORAGE_FILE.exists():
                err_console.print("File does not exist. Try saving first.")
            else:
                with STORAGE_FILE.open("rb") as file:
                    self.google: Google = pickle.load(file)
                console.print("Load succeeded.", style="info")
        else:
            console.print("Load terminated.", style="info")

    def get_domain(self, name: str) -> Domain:
        domain: Domain | None = self.google.domains.get(name)
        if domain is None:
            ans: bool = questionary.confirm(
                "Do you confirm the creation of a new domain?"
            ).ask()
            if ans:
                new_domain: Domain = Domain(name=name)
                self.google.add_domains(new_domain)
                return new_domain
            else:
                raise ValueError("Domain creation rejected")
        else:
            return domain

    def format_name(self, name: str) -> str:
        return re.sub(r"\W", "", name).lower()

    def format_text(self, text: str) -> str:
        return text.replace("\\n", "\n").replace("\\t", "\t")

    def parse_url(self, url: str) -> tuple[str, str]:
        regex = r"[a-z0-9]+"

        parts = url.strip().lower().split(".")

        if len(parts) != 2 or not all(parts):
            raise ValueError("Enter a URL in the form name.domain")

        name, domain = parts

        if not re.fullmatch(regex, name):
            raise ValueError("Invalid website name")

        if not re.fullmatch(regex, domain):
            raise ValueError("Invalid domain")

        return name, domain


def main():
    console.clear()
    t: TextUI = TextUI("The Web")
    menu: WebsiteMenu = WebsiteMenu()
    t.bind(menu.search)
    t.bind(menu.add_website)
    t.bind(menu.display_websites)
    t.bind(menu.save)
    t.bind(menu.load)
    t.loop()


if __name__ == "__main__":
    main()
