# Standard imports
import logging
import platform
from dataclasses import asdict
from datetime import datetime, timezone
from pathlib import Path
from zipfile import ZipFile

# Third party imports
import click
import tomlkit
from pydantic.dataclasses import dataclass

# Lexer imports
from lexer.artifact.lexer_artifact import LexerArtifact
from lexer.configs.config import AllConfig
from lexer.version import LEXER_SDK_VERSION

logger = logging.getLogger(__name__)


@dataclass
class LexerArtifactManager:
    lexer_artifact = LexerArtifact(file_paths=[])
    metadata_doc = tomlkit.document()

    def initialize_artifact_metadata(self):
        """
        This internal method initializes the title and created_at / updated_at
        parameters in the Lexer artifact metadata file.
        """
        self.metadata_doc.add(
            tomlkit.comment(
                "This is an autogenerated TOML Lexer artifact file."
            )
        )
        self.metadata_doc.add(
            tomlkit.comment("Please do not modify it directly.")
        )
        self.metadata_doc.add(tomlkit.nl())
        self.metadata_doc["title"] = "unknown"
        self.metadata_doc.add(tomlkit.nl())

        # Timestamps in UTC.
        utcnow = str(datetime.now(timezone.utc).isoformat())
        self.metadata_doc.add("created_at", utcnow)  # type: ignore
        self.metadata_doc.add("updated_at", utcnow)  # type: ignore
        self.metadata_doc["created_at"].comment("in UTC")  # type: ignore
        self.metadata_doc["updated_at"].comment("in UTC")  # type: ignore
        self.metadata_doc.add(tomlkit.nl())

        # Lexer versions + platform info.
        env_info = tomlkit.table()
        env_info.add("lexer_sdk_version", LEXER_SDK_VERSION)
        env_info.add("python_version", platform.python_version())
        env_info.add("system", platform.system())
        env_info.add("release", platform.release())
        self.metadata_doc.add("environment", env_info)

    def generate_lexer_artifact_filename(self, name: str) -> Path:
        """
        Internal method for generating Lexer artifact filename.
        Returns a pathlib.Path
        """
        output_filename = Path("{name}.lexer".format(name=name))
        self.metadata_doc["title"] = str(output_filename)
        return output_filename

    def init(self):
        """
        This method initializes / reinitializes a new Lexer file context.
        """
        self.initialize_artifact_metadata()

    def load(self, lexer_artifact_path: str):
        """
        This method loads a current LexerArtifact.
        NOTE: It will also reinitialize the Lexer file context. This means you
        may lose context on unsaved / unexported Lexer files.
        """
        logger.info("Reinitialize the Lexer context...")
        self.init()

        # TODO(LEX-33): Implement proper loading of Lexer artifact from a file
        #               path.
        logger.info(
            "Loading Lexer artifact: {lexer_artifact_path}...".format(
                lexer_artifact_path=lexer_artifact_path
            )
        )
        # self.lexer_artifact = lexer_artifact

    def append(self, file: Path):
        """
        This method appends a file to the list of file paths in Lexer artifact.

        Params
        ------
        file: Path - The file path to be embedded within Lexer artifact.
        """
        # TODO(LEX-39): Verify if .load() file path to Lexer artifact is valid
        self.lexer_artifact.file_paths.append(file)
        # Add metadata associated with it too.

    def export(self, name: str, target_directory: str):
        """
        This method takes in a name for the Lexer output artifact and target
        directory for where to place it.

        Params
        ------
        name: str - The name of the Lexer artifact.
        For example, "my_benchmarks" to export a "my_benchmarks.lexer"
        target_directory: str - The target directory for where to place this
        generated ".lexer" artifact.
        """
        lexer_artifact_filename = self.generate_lexer_artifact_filename(
            name=name
        )
        output_lexer_artifact_path = Path(target_directory).joinpath(
            lexer_artifact_filename
        )
        logger.info(
            "The following files will be zipped into {output_file}:".format(
                output_file=output_lexer_artifact_path
            )
        )
        for file_name in self.lexer_artifact.file_paths:
            logger.info(file_name)

        # TODO: Update timestamps in the metadata file here.

        click.secho(
            "Packing Lexer output artifacts...", fg="yellow", bold=True
        )
        with ZipFile(output_lexer_artifact_path, "w") as zip:
            # writing each output file one by one
            for file in self.lexer_artifact.file_paths:
                zip.write(file, Path(file).name)

            # writing the metadata file too.
            output_metadata_filename = "/tmp/{name}.toml".format(name=name)
            with Path(output_metadata_filename).open("w") as fout:
                fout.write(tomlkit.dumps(self.metadata_doc))
            zip.write(
                output_metadata_filename,
                Path(output_metadata_filename).name,
            )

        click.secho(
            "Results generated and exported to {output_lexer_artifact_path}!".format(  # noqa: E501
                output_lexer_artifact_path=output_lexer_artifact_path
            ),
            fg="green",
            bold=True,
        )

    def update_artifact_metadata_section(
        self, key: str, model_config: AllConfig
    ):
        section = tomlkit.table()
        section.add("config", asdict(model_config))
        self.metadata_doc.add(key, section)
