import tempfile
import pathlib
import importlib
import importlib.machinery
import importlib.util
import io
import os
import sys
import shutil
from typing import Any

import pdfCropMargins
import ruamel.yaml
import pypdfium2

# Import rendercv. I import the data_models and renderer modules like this instead
# of using `import rendercv` because in order for that to work, the current working
# directory must be the root of the project. To make it convenient for the user, I
# import the modules using the full path of the files.
repository_root = pathlib.Path(__file__).parent.parent
rendercv_path = repository_root / "rendercv"
image_assets_directory = pathlib.Path(__file__).parent / "assets" / "images"


def import_a_module(module_name: str, file_path: pathlib.Path):
    """Imports a module from a file.

    Args:
        module_name (str): The name of the module.
        file_path (pathlib.Path): The path to the file.
    Returns:
        Any: The imported module.
    """
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    module = importlib.util.module_from_spec(spec)  # type: ignore
    sys.modules[module_name] = module
    spec.loader.exec_module(module)  # type: ignore
    return module


# Import rendercv:
rendercv = import_a_module("rendercv", rendercv_path / "__init__.py")

# Import the rendercv.data_models as dm:
dm = import_a_module("rendercv.data_models", rendercv_path / "data_models.py")

# Import the rendercv.renderer as r:
r = import_a_module("rendercv.renderer", rendercv_path / "renderer.py")

# Import the rendercv.cli as cli:
cli = import_a_module("rendercv.cli", rendercv_path / "cli.py")

# The entries below will be pasted into the documentation as YAML, and their
# corresponding figures will be generated with this script.
education_entry = {
    "institution": "Boğaziçi University",
    "location": "Istanbul, Turkey",
    "degree": "BS",
    "area": "Mechanical Engineering",
    "start_date": "2015-09",
    "end_date": "2020-06",
    "highlights": [
        "GPA: 3.24/4.00 ([Transcript](https://example.com))",
        "Awards: Dean's Honor List, Sportsperson of the Year",
    ],
}

experience_entry = {
    "company": "Some Company",
    "location": "TX, USA",
    "position": "Software Engineer",
    "start_date": "2020-07",
    "end_date": "2021-08-12",
    "highlights": [
        (
            "Developed an [IOS application](https://example.com) that has received"
            " more than **100,000 downloads**."
        ),
        "Managed a team of **5** engineers.",
    ],
}

normal_entry = {
    "name": "Some Project",
    "location": "Remote",
    "date": "2021-09",
    "highlights": [
        "Developed a web application with **React** and **Django**.",
        "Implemented a **RESTful API**",
    ],
}

publication_entry = {
    "title": (
        "Magneto-Thermal Thin Shell Approximation for 3D Finite Element Analysis of"
        " No-Insulation Coils"
    ),
    "authors": ["J. Doe", "***H. Tom***", "S. Doe", "A. Andsurname"],
    "date": "2021-12-08",
    "journal": "IEEE Transactions on Applied Superconductivity",
    "doi": "10.1109/TASC.2023.3340648",
}

one_line_entry = {
    "label": "Programming",
    "details": "Python, C++, JavaScript, MATLAB",
}

bullet_entry = {
    "bullet": "This is a bullet entry.",
}

text_entry = (
    "This is a *TextEntry*. It is only a text and can be useful for sections like"
    " **Summary**. To showcase the TextEntry completely, this sentence is added, but it"
    " doesn't contain any information."
)


def dictionary_to_yaml(dictionary: dict[str, Any]):
    """Converts a dictionary to a YAML string.

    Args:
        dictionary (dict[str, Any]): The dictionary to be converted to YAML.
    Returns:
        str: The YAML string.
    """
    yaml_object = ruamel.yaml.YAML()
    yaml_object.width = 60
    yaml_object.indent(mapping=2, sequence=4, offset=2)
    with io.StringIO() as string_stream:
        yaml_object.dump(dictionary, string_stream)
        yaml_string = string_stream.getvalue()
    return yaml_string


def define_env(env):
    # see https://mkdocs-macros-plugin.readthedocs.io/en/latest/macros/
    entries = [
        "education_entry",
        "experience_entry",
        "normal_entry",
        "publication_entry",
        "one_line_entry",
        "bullet_entry",
        "text_entry",
    ]

    entries_showcase = dict()
    for entry in entries:
        proper_entry_name = entry.replace("_", " ").title()
        entries_showcase[proper_entry_name] = {
            "yaml": dictionary_to_yaml(eval(entry)),
            "figures": [
                {
                    "path": f"assets/images/{theme}/{entry}.png",
                    "alt_text": f"{proper_entry_name} in {theme}",
                    "theme": theme,
                }
                for theme in dm.available_themes
            ],
        }

    env.variables["showcase_entries"] = entries_showcase


def generate_entry_figures():
    """Generate an image for each entry type and theme."""
    # Generate PDF figures for each entry type and theme
    entries = {
        "education_entry": dm.EducationEntry(**education_entry),
        "experience_entry": dm.ExperienceEntry(**experience_entry),
        "normal_entry": dm.NormalEntry(**normal_entry),
        "publication_entry": dm.PublicationEntry(**publication_entry),
        "one_line_entry": dm.OneLineEntry(**one_line_entry),
        "text_entry": f"{text_entry}",
        "bullet_entry": dm.BulletEntry(**bullet_entry),
    }
    themes = dm.available_themes

    with tempfile.TemporaryDirectory() as temporary_directory:
        # create a temporary directory:
        temporary_directory_path = pathlib.Path(temporary_directory)
        for theme in themes:
            for entry_type, entry in entries.items():
                design_dictionary = {
                    "theme": theme,
                    "disable_page_numbering": True,
                    "show_last_updated_date": False,
                }
                if theme == "moderncv":
                    # moderncv theme does not support these options:
                    del design_dictionary["disable_page_numbering"]
                    del design_dictionary["show_last_updated_date"]

                # Create the data model with only one section and one entry
                data_model = dm.RenderCVDataModel(
                    **{
                        "cv": dm.CurriculumVitae(sections={entry_type: [entry]}),
                        "design": design_dictionary,
                    }
                )

                # Render:
                latex_file_path = r.generate_latex_file_and_copy_theme_files(
                    data_model, temporary_directory_path
                )
                pdf_file_path = r.latex_to_pdf(latex_file_path)

                # Prepare the output directory and file path:
                output_directory = image_assets_directory / theme
                output_directory.mkdir(parents=True, exist_ok=True)
                output_pdf_file_path = output_directory / f"{entry_type}.pdf"

                # Remove the file if it exists:
                if output_pdf_file_path.exists():
                    output_pdf_file_path.unlink()

                # Crop the margins
                pdfCropMargins.crop(
                    argv_list=[
                        "-p4",
                        "100",
                        "0",
                        "100",
                        "0",
                        "-a4",
                        "0",
                        "-30",
                        "0",
                        "-30",
                        "-o",
                        str(output_pdf_file_path.absolute()),
                        str(pdf_file_path.absolute()),
                    ]
                )

                # Convert pdf to an image
                image_name = output_pdf_file_path.with_suffix(".png")
                pdf = pypdfium2.PdfDocument(str(output_pdf_file_path.absolute()))
                page = pdf[0]
                image = page.render(scale=5).to_pil()

                # If the image exists, remove it
                if image_name.exists():
                    image_name.unlink()

                image.save(image_name)

                pdf.close()

                # Remove the pdf file
                output_pdf_file_path.unlink()


def generate_examples():
    """Generate example YAML and PDF files."""
    examples_directory_path = pathlib.Path(__file__).parent.parent / "examples"

    # check if the examples directory exists, if not create it
    if not examples_directory_path.exists():
        examples_directory_path.mkdir()

    os.chdir(examples_directory_path)
    themes = dm.available_themes
    for theme in themes:
        cli.cli_command_new("John Doe", theme)
        yaml_file_path = examples_directory_path / "John_Doe_CV.yaml"

        # Rename John_Doe_CV.yaml:
        proper_theme_name = theme.replace("_", " ").title() + "Theme"
        new_yaml_file_path = (
            examples_directory_path / f"John_Doe_{proper_theme_name}_CV.yaml"
        )
        if new_yaml_file_path.exists():
            new_yaml_file_path.unlink()
        yaml_file_path.rename(new_yaml_file_path)
        yaml_file_path = new_yaml_file_path

        # Generate the PDF file:
        cli.cli_command_render(yaml_file_path)

        output_pdf_file = (
            examples_directory_path / "rendercv_output" / "John_Doe_CV.pdf"
        )

        # Move pdf file to the examples directory:
        new_pdf_file_path = examples_directory_path / f"{yaml_file_path.stem}.pdf"
        if new_pdf_file_path.exists():
            new_pdf_file_path.unlink()
        output_pdf_file.rename(new_pdf_file_path)

        # Remove the rendercv_output directory:
        rendercv_output_directory = examples_directory_path / "rendercv_output"

        shutil.rmtree(rendercv_output_directory)

        # convert first page of the pdf to an image:
        pdf = pypdfium2.PdfDocument(str(new_pdf_file_path.absolute()))
        page = pdf[0]
        image = page.render(scale=2).to_pil()
        image_path = image_assets_directory / f"{theme}.png"
        image.save(image_path)


def generate_schema():
    """Generate the schema."""
    json_schema_file_path = repository_root / "schema.json"
    dm.generate_json_schema_file(json_schema_file_path)


def update_index():
    """Update the index.md file by copying the README.md file."""
    index_file_path = repository_root / "docs" / "index.md"
    readme_file_path = repository_root / "README.md"
    shutil.copy(readme_file_path, index_file_path)


if __name__ == "__main__":
    generate_entry_figures()
    generate_examples()
    generate_schema()
    update_index()
