# TODO
# mypy strict_optional=False

import sys
import os
from os import path

from .configuration import (
    SfgConfiguration,
    config_from_commandline,
    merge_configurations,
)
from .context import SfgContext


class SourceFileGenerator:
    """Context manager that controls the code generation process in generator scripts.

    ## Configuration

    The [source file generator][pystencilssfg.SourceFileGenerator] draws configuration from a total of four sources:

     - The [default configuration][pystencilssfg.configuration.DEFAULT_CONFIG];
     - The project configuration;
     - Command-line arguments;
     - The user configuration passed to the constructor of `SourceFileGenerator`.

    They take precedence in the following way:

     - Project configuration overrides the default configuration
     - Command line arguments override the project configuration
     - User configuration overrides default and project configuration,
       and must not conflict with command-line arguments; otherwise, an error is thrown.

    ### Project Configuration via Configurator Script

    Currently, the only way to define the project configuration is via a configuration module.
    A configurator module is a Python file defining the following function at the top-level:

    ```Python
    from pystencilssfg import SfgConfiguration

    def sfg_config() -> SfgConfiguration:
        # ...
        return SfgConfiguration(
            # ...
        )
    ```

    The configuration module is passed to the code generation script via the command-line argument
    `--sfg-config-module`.
    """

    def __init__(self, sfg_config: SfgConfiguration | None = None):
        if sfg_config and not isinstance(sfg_config, SfgConfiguration):
            raise TypeError("sfg_config is not an SfgConfiguration.")

        import __main__

        scriptpath = __main__.__file__
        scriptname = path.split(scriptpath)[1]
        basename = path.splitext(scriptname)[0]

        project_config, cmdline_config, script_args = config_from_commandline(sys.argv)

        config = merge_configurations(project_config, cmdline_config, sfg_config)

        self._context = SfgContext(
            config.outer_namespace, config.codestyle, argv=script_args
        )

        from .emission import HeaderImplPairEmitter

        self._emitter = HeaderImplPairEmitter(config.get_output_spec(basename))

    def clean_files(self):
        for file in self._emitter.output_files:
            if path.exists(file):
                os.remove(file)

    def __enter__(self) -> SfgContext:
        self.clean_files()
        return self._context

    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type is None:
            self._emitter.write_files(self._context)