"""supporting functions"""

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

# %% auto 0
__all__ = ['rename_filepath_to_match_datatype', 'change_file_suffix', 'detect_encoding', 'read_html_file_as_soup',
           'upsert_folder', 'upsert_file', 'get_all_files_and_folders', 'update_env', 'output_content_and_update_env',
           'ImageBlockConverter', 'md', 'convert_html_to_markdown', 'convert_remove_html_tags', 'download_zip',
           'download_pptx', 'download_markdown', 'generate_filename_from_url']

# %% ../../nbs/utils/files.ipynb 3
from typing import List, Tuple, Union, Callable, Any

import os
import json
import chardet
import dotenv

import pathlib
import shutil

from bs4 import BeautifulSoup
from markdownify import MarkdownConverter
import PIL

import datetime as dt


import pptx2md

import zipfile
import io

from urllib.parse import urlparse

# %% ../../nbs/utils/files.ipynb 7
def rename_filepath_to_match_datatype(data, file_path):
    is_path_ext = os.path.splitext(file_path)[-1].lower()

    old_suffix = pathlib.Path(file_path).suffix if is_path_ext else None

    new_suffix = ""

    if isinstance(data, str) or isinstance(data, bytes) or isinstance(data, bytearray):
        new_suffix = ".txt"
    if isinstance(data, dict):
        new_suffix = ".json"

    file_path = file_path + new_suffix

    if old_suffix:
        file_path = file_path.replace(old_suffix, "")

    return file_path

# %% ../../nbs/utils/files.ipynb 9
def change_file_suffix(filename, new_extension, change_if_exists: bool = True):
    is_exist = os.path.exists(filename)

    p = pathlib.PurePath(filename)
    p = p.with_suffix(new_extension)

    if is_exist and change_if_exists:
        p = pathlib.Path(filename)
        p = p.rename(p.with_suffix(new_extension))

    return str(p)

# %% ../../nbs/utils/files.ipynb 11
def detect_encoding(file_path, debug_prn: bool = False):
    detector = chardet.universaldetector.UniversalDetector()
    with open(file_path, "rb") as f:
        for line in f:
            detector.feed(line)
            if detector.done:
                break
    detector.close()

    encoding = detector.result

    return encoding

# %% ../../nbs/utils/files.ipynb 13
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()

# %% ../../nbs/utils/files.ipynb 16
def upsert_folder(folder_path: str, debug_prn: bool = False, replace_folder=False):

    folder_path = os.path.dirname(folder_path)

    if replace_folder and os.path.exists(folder_path) and os.path.isdir(folder_path):
        shutil.rmtree(folder_path)

    if debug_prn:
        print(
            {
                "upsert_folder": os.path.abspath(folder_path),
                "is_exist": os.path.exists(folder_path),
            }
        )

    if not os.path.exists(folder_path):
        os.makedirs(folder_path)

# %% ../../nbs/utils/files.ipynb 17
def upsert_file(
    file_path: str,
    content: Any = None,
    write_fn: Callable = None,
    file_update_method="w",
    encoding="utf-8",
    debug_prn: bool = False,
    replace_folder: bool = False,
) -> bool:

    upsert_folder(
        folder_path=file_path, debug_prn=debug_prn, replace_folder=replace_folder
    )

    with open(file_path, file_update_method, encoding=encoding) as file:
        if not write_fn:
            file.write(content or "")
            return True

        return write_fn(file=file, content=content)

# %% ../../nbs/utils/files.ipynb 18
def get_all_files_and_folders(
    directory, file_type=None  # to only retrieve a specific file type
) -> Union[Tuple, List]:
    """
    walk a directory and retrieve a list of files and a list of directory
    returns Tuple of (file_ls , dir_ls) OR file_ls if file_type supplied
    """
    if not os.path.exists(directory):
        raise FileNotFoundError(directory)

    file_ls = []
    dir_ls = []
    for root, dirs, files in os.walk(directory):
        for name in files:
            if file_type:
                if not name.lower().endswith(file_type.lower()):
                    continue
            file_ls.append(os.path.join(root, name))

        if file_type:
            continue

        for name in dirs:
            dir_ls.append(os.path.join(root, name))

    if file_type:
        return file_ls

    return file_ls, dir_ls

# %% ../../nbs/utils/files.ipynb 21
def update_env(
    env_path: str, env_key: str, content: str, debug_prn: bool = False
) -> dict:
    """
    updates a .env file with a key value pair
    then reloads the env_file
    """

    if not os.path.exists(env_path):
        with open(env_path, "w", encoding="utf-8") as f:
            f.write("")

    quote_mode = "always"

    if isinstance(content, dict):
        quote_mode = "never"
        content = json.dumps(content)

    if debug_prn:
        from pprint import pprint

        pprint(
            {
                "env_path": env_path,
                "key": env_key,
                "value": content,
                "type": type(content),
                "quote_mode": quote_mode,
            }
        )

    dotenv.set_key(env_path, env_key, content, quote_mode=quote_mode)

    dotenv.set_key(env_path, "env_last_modified", f"updated - {dt.date.today()}")

    dotenv.load_dotenv(env_path, override=True)

    return {env_key: os.getenv(env_key)}

# %% ../../nbs/utils/files.ipynb 22
def output_content_and_update_env(
    content, output_path=None, env_path=None, env_key=None, debug_prn: bool = False
):
    if env_key:
        os.environ[env_key] = content

    if output_path:
        upsert_file(file_path=output_path, content=content, debug_prn=debug_prn)

    if env_path:
        update_env(
            env_path=env_path, env_key=env_key, content=content, debug_prn=debug_prn
        )

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

    def convert_img(self, el, text, convert_as_inline, is_resize: bool = True):
        """
        custom image downloader for ImabeBlockConverter
        will handle resize
        """

        if is_resize:
            style_obj = {
                (obj.split(":")[0].strip()): obj.split(":")[1].strip()
                for obj in el.get("style").split(";")
                if ":" in obj
            }

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

            image = PIL.Image.open(file_path)

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

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

            new_image = image.resize((width, height))
            new_image.save(file_path)

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


def md(html, **options):
    """Create shorthand method for handling conversion"""
    return ImageBlockConverter(**options).convert(html)

# %% ../../nbs/utils/files.ipynb 25
def convert_html_to_markdown(file_path):
    """converts html file to markdown in place"""

    with open(file_path, encoding="utf-8") as f:
        html = f.read()

    markdown_content = md(
        str(html),
        keep_inline_images_in=["td", "span"],
        file_path=file_path,
        is_resize=True,
    )

    md_path = file_path.replace(".html", ".md")

    with open(md_path, "w+", encoding="utf-8") as f:
        f.write(markdown_content)

    return

# %% ../../nbs/utils/files.ipynb 26
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/files.ipynb 28
def download_zip(
    content,
    output_folder,
    is_convert_to_markdown: bool = True,
    replace_folder=True,
    **kwargs,
):
    """save bytes content to a zip file then convert html to markdown"""

    upsert_folder(folder_path=output_folder, replace_folder=replace_folder)

    zip = zipfile.ZipFile(io.BytesIO(content), "r")
    zip.extractall(output_folder)

    file_ls = os.listdir(output_folder)

    # rename the html file to index.html
    for file_name in file_ls:
        if file_name.endswith(".html"):
            output_index = os.path.join(output_folder, "index.html")
            os.replace(os.path.join(output_folder, file_name), output_index)

            if is_convert_to_markdown:
                convert_html_to_markdown(os.path.join(output_folder, "index.html"))

    return f"successfully downloaded zip to {output_folder}"

# %% ../../nbs/utils/files.ipynb 31
def download_pptx(
    content,
    output_folder,
    is_convert_to_markdown: bool = True,
    replace_folder: bool = True,
    **kwargs,
):
    """save bytes content to a pptx file then converts to markdown"""

    output_ppt_index = os.path.join(output_folder, "index.pptx")

    upsert_file(output_ppt_index, replace_folder=replace_folder)

    with open(output_ppt_index, "wb+") as binary_file:
        # Write bytes to file
        binary_file.write(content)

    if is_convert_to_markdown:
        pptx2md.convert(
            pptx2md.ConversionConfig(
                pptx_path=output_ppt_index,
                output_path=os.path.join(output_folder, "index.md"),
                image_dir=os.path.join(output_folder, "images"),
            )
        )

    return f"successfully downloaded content to {output_folder}"

# %% ../../nbs/utils/files.ipynb 34
def download_markdown(content, output_folder, replace_folder: bool = True):

    upsert_file(
        file_path=output_folder, content=md(content), replace_folder=replace_folder
    )

# %% ../../nbs/utils/files.ipynb 36
def generate_filename_from_url(url, download_folder=None, file_name=None) -> str:
    parsed_url = urlparse(url)

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

    if download_folder:
        file_path = os.path.join(download_folder, file_path)

    if file_name:
        file_path = os.path.join(file_path, file_name)

    return file_path
