"""supporting functions"""

# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/utils/web.ipynb.

# %% auto 0
__all__ = ['remove_query_params_from_url', 'generate_filename_from_url', 'generate_file_path_from_doc_title',
           'extract_canonical_url_to_html', 'insert_canonical_url_to_html', 'processing_fn', 'clean_bs4',
           'read_html_file_as_soup', 'convert_remove_html_tags', 'extract_links', 'ImageBlockConverter', 'md']

# %% ../../nbs/utils/web.ipynb 2
from ..client import LibraryError

# %% ../../nbs/utils/web.ipynb 4
from bs4 import BeautifulSoup
from markdownify import MarkdownConverter
from urllib.parse import urlparse, urlunparse, urljoin

from PIL import Image
from typing import List, Callable, Union, List

import re

import os

import domolibrary_extensions.utils.files as defi
import domolibrary_extensions.utils.convert as decv

# %% ../../nbs/utils/web.ipynb 5
def remove_query_params_from_url(url):
    u = urlparse(url)
    return urljoin(url, urlparse(url).path)

# %% ../../nbs/utils/web.ipynb 7
def generate_filename_from_url(
    url,
    download_folder=None,
    use_separator="/",
    max_length=None,
    file_name=None,
    suffix=None,
) -> str:
    parsed_url = urlparse(remove_query_params_from_url(url))

    file_path = [str for str in parsed_url[2].split("/") if str][:max_length]

    if download_folder:
        file_path[0:0] = [download_folder]

    if file_name:
        file_path.append(file_name)

    if suffix:
        file_path[-1] = defi.change_file_suffix(file_path[-1], suffix)

    file_path = use_separator.join(file_path)

    return file_path


def generate_file_path_from_doc_title(
    text_str, export_folder=None, is_index: bool = False
) -> str:
    """generates a clean file path given a text string (usually the name of the google doc)"""

    file_name = "index.md"

    if not is_index:
        file_name = decv.convert_str_file_name(
            decv.convert_str_to_snake_case(
                decv.convert_str_remove_emojis(text_str).strip()
            )
        ).replace("-", "")

    if export_folder:
        return os.path.join(export_folder, file_name)

    return file_name

# %% ../../nbs/utils/web.ipynb 9
def extract_canonical_url_to_html(html_file_path: str):
    assert os.path.exists(html_file_path)
    # Load the HTML file

    with open(html_file_path, "r", encoding="utf-8") as f:
        soup = BeautifulSoup(f, "html.parser")

    # Find the canonical link tag
    canonical_tag = soup.find("link", rel="canonical")

    # Extract the URL
    if not canonical_tag or not canonical_tag.has_attr("href"):
        raise LibraryError(f"unable to retrieve cannonical_url from {html_file_path}")

    return canonical_tag["href"]


def insert_canonical_url_to_html(
    html_file_path: str, url: str, is_strip_params: bool = True
):
    # Define the file path and canonical URL

    def _strip_params(url):
        # Parse the URL
        parsed = urlparse(url)

        # Strip query params (keep only scheme, netloc, path)
        return urlunparse((parsed.scheme, parsed.netloc, parsed.path, "", "", ""))

    assert os.path.exists(html_file_path)

    # Load the HTML content
    with open(html_file_path, "r", encoding="utf-8") as f:
        soup = BeautifulSoup(f, "html.parser")

    # Create the canonical link tag
    if is_strip_params:
        url = _strip_params(url)
    canonical_tag = soup.new_tag("link", rel="canonical", href=url)

    # Insert into <head>, if it exists
    if soup.head:
        # Avoid duplicates
        if not soup.head.find("link", rel="canonical"):
            soup.head.append(canonical_tag)
    else:
        # If no <head>, create it
        head = soup.new_tag("head")
        head.append(canonical_tag)
        soup.insert(0, head)

    # Save the updated HTML
    with open(html_file_path, "w", encoding="utf-8") as f:
        f.write(str(soup))


def processing_fn(soup, url: str):

    def _upsert_self_gdoc_tag(soup, url) -> BeautifulSoup:
        """finds all self godc hrefs in document and replaces with url"""

        gdoc_tags = soup.find_all(["span"], string=re.compile("self gdoc", re.I))

        if not gdoc_tags:
            # print("not godoc tags")

            try:
                self_tag = soup.new_tag("a", href=url)
                self_tag.string = "Self GDoc"
                soup.append(self_tag)

            except Exception as e:
                message = f"processing_fn, {e}"
                print(message)

            return soup

        for tag in gdoc_tags:
            if not tag or not tag.find("a"):
                print("no href", tag)
                continue

            # print("updating gdoc tags")
            # tag_url = tag.find("a").get("href", None)
            tag.find("a").href = url

        return soup

    def _remove_superscripts(soup) -> BeautifulSoup:
        for sup in soup.find_all("sup"):
            sup.decompose()

        return soup

    # print("using default cleanup processing_fn")

    _remove_superscripts(soup)

    _upsert_self_gdoc_tag(soup=soup, url=url)

    return soup


def clean_bs4(
    html, is_body: bool = False, processing_fn: Callable = None
) -> BeautifulSoup:
    """all google docs content will be downloaded as HTML and then parsed using beautiful soup

    optionally can passing a processing_fn that receives soup
    """

    soup = BeautifulSoup(html, "html.parser")

    if is_body:
        soup = soup.find("body")

        if not soup:
            raise LibraryError("clean_bs4 - No body found")

    if processing_fn:
        soup = processing_fn(soup=soup)

    return soup


def read_html_file_as_soup(
    file_path, is_convert_to_soup: bool = True
) -> Union[str, BeautifulSoup]:

    if not os.path.exists(file_path):
        raise FileNotFoundError(file_path)

    page_encoding = detect_encoding(file_path)

    with open(file_path, encoding=page_encoding["encoding"]) as fp:
        if is_convert_to_soup:
            return BeautifulSoup(fp, "lxml")

        return fp.read()


def convert_remove_html_tags(html_string: str) -> str:
    """
    Clean an HTML string using BeautifulSoup.

    Args:
        html_string (str): The HTML string.

    Returns:
        str: The cleaned string.
    """
    soup = BeautifulSoup(html_string, "html.parser")
    clean_string = soup.get_text()
    return clean_string

# %% ../../nbs/utils/web.ipynb 10
def extract_links(
    soup: BeautifulSoup,
    base_url: str = None,
    custom_link_extractor_fn: Callable = None,  # can add custom function for handling URLs
) -> List[Union[str, dict]]:
    """returns a list of urls"""

    links_ls = []

    for link in soup.findAll("a"):
        if not link.has_attr("href"):
            continue

        url = link["href"]

        if custom_link_extractor_fn:
            url = custom_link_extractor_fn(url, base_url)

        if not url:
            continue

        if url.startswith("/") and base_url:
            url = urljoin(base_url, url)

        if base_url and not url.startswith(base_url):
            continue

        if not url or url in links_ls:
            continue

        links_ls.append(url)

    return list(set(links_ls))

# %% ../../nbs/utils/web.ipynb 11
class ImageBlockConverter(MarkdownConverter):
    """
    Create a custom MarkdownConverter that adds two newlines after an image
    """

    def convert_img(self, el, text, parent_tags):

        file_path = os.path.join(os.path.dirname(self.options["file_path"]), el["src"])
        defi.upsert_folder(file_path)

        image = Image.open(file_path)

        style_ls = el.get("style")

        if style_ls:
            style_obj = {
                obj.split(":")[0].strip(): obj.split(":")[1].strip()
                for obj in style_ls.split(";")
                if obj and ":" in obj
            }

            width = style_obj["width"].replace("px", "")
            width = int(float(width))

            height = style_obj["height"].replace("px", "")
            height = int(float(height))

            image = image.resize((width, height))

        image.save(file_path)

        return super().convert_img(el, text, parent_tags)


def md(html, **options):
    return ImageBlockConverter(**options).convert(html)
