# Website.py
# Website and domain modeling
from __future__ import annotations

import time

from rich.console import Console
from rich.live import Live
from rich.theme import Theme
from rich.traceback import install

theme = Theme({"info": "bright_cyan"})

console = Console(theme=theme)
err_console = Console(stderr=True, style="red italic")

install(console=err_console, show_locals=True)

class Domain:
    def __init__(self, name: str) -> None:
        self.name: str = name
        self.websites: dict[str, Website] = {}
        self.registrar: Google | None = None

    def __str__(self) -> str:
        return self.name

    def add(self, website: Website) -> None:
        if not website.name:
            raise ValueError("Website must have a name")
        if website.name in self.websites:
            raise ValueError(f"Website '{website.name} already exists' "
                             f"in domain '{self.name}'")
        self.websites[website.name] = website

    def register(self, registrar: Google) -> None:
        self.registrar = registrar

    def find_site(self, name: str) -> Website | None:
        return self.websites.get(name)


class Website:
    def __init__(self, text: str) -> None:
        self.text: str = text
        self.domain: Domain | None = None
        self.name: str | None = None

    def __str__(self) -> str:
        return self.url

    @property
    def url(self) -> str:
        if self.published:
            return f"{self.name}.{self.domain}"
        else:
            return "Unpublished"

    @property
    def published(self) -> bool:
        val = True
        if self.domain is None or self.name is None:
            val = False

        return val

    def publish(self, name: str, domain: Domain) -> None:
        self.name = name
        self.domain = domain
        self.domain.add(self)

    def visit(self) -> None:
        if self.published:
            console.print(f"\t\t[underline]Welcome to {self.url}[/]\n\n{self.text}\n")
        else:
            console.print(self.url)


class Person:
    def __init__(self, name: str, website: Website | None = None) -> None:
        self.name = name
        self.website: Website | None
        if website:
            self.set_website(website)
        else:
            self.website = None

    def set_website(self, website: Website) -> None:
        self.website = website

    def publish(self, domain: Domain) -> None:
        if self.website is None:
            raise ValueError("Website must be set before publishing")
        self.website.publish(self.name.lower(), domain)


class Google:
    delay: int = 1

    def __init__(self, *domains: Domain) -> None:
        self.domains: dict[str, Domain] = {}
        self.add_domains(*domains)

    def add_domains(self, *domains: Domain) -> None:
        for domain in domains:
            if domain.name in self.domains:
                raise ValueError(f"Domain '{domain.name}' already exists")

        for domain in domains:
            self.domains[domain.name] = domain
            domain.register(self)

    def search(self, url: str) -> None:
        with Live(console=console, auto_refresh=False) as live:
            console.print(f"Searching '{url}'...", end="")
            time.sleep(self.delay)
            site: Website | None = self.find_site(url)
            live.refresh()
            if site is None:
                err_console.print(f"'{url}' not found")
                return
            site.visit()

    def find_site(self, url: str) -> Website | None:
        name, domain = self.process_url(url)
        if (name, domain) == (None, None):
            return None
        return domain.find_site(name)

    def process_url(self, url: str) -> tuple:
        parts = url.strip().lower().split(".")

        if len(parts) != 2 or not all(parts):
            return None, None

        name, domain_name = parts
        domain = self.domains.get(domain_name)

        if domain is None:
            return None, None

        return name, domain
