import logging
import os
from contextlib import contextmanager
from pathlib import Path
from tempfile import NamedTemporaryFile
from typing import Dict
from typing import Generator
from typing import Optional
from typing import Union

from PyMca5.PyMcaIO.ConfigDict import ConfigDict

from ._modify.beam_energy import set_beam_energy
from ._modify.fast_fitting import force_fast_fitting
from ._modify.fit_weights import set_fit_weights
from ._modify.quantification import enable_quantification
from ._modify.split_mlines import split_mlines
from ._modify.summary import configuration_summary
from .model import PyMcaXrfConfiguration

logger = logging.getLogger(__name__)


def pymca_configdict_from_model(configuration: PyMcaXrfConfiguration) -> ConfigDict:
    data = configuration.model_dump(mode="python", exclude_unset=True)
    return ConfigDict(initdict=data)


def pymca_configdict_to_model(
    configuration: Union[ConfigDict, dict],
) -> PyMcaXrfConfiguration:
    return PyMcaXrfConfiguration(**configuration)


def pymca_configdict_from_file(filename: Union[str, Path]) -> ConfigDict:
    return ConfigDict(filelist=[str(filename)])


def pymca_configdict_to_file(
    configuration: ConfigDict, filename: Union[str, Path]
) -> None:
    configuration.write(str(filename))


@contextmanager
def temporary_pymca_configdict_to_file(
    configuration: ConfigDict,
) -> Generator[str, None, None]:
    tmp = NamedTemporaryFile(suffix=".cfg", delete=False)
    try:
        tmp.close()
        pymca_configdict_to_file(configuration, tmp.name)
        yield tmp.name
    finally:
        try:
            os.remove(tmp.name)
        except FileNotFoundError:
            pass


def modify_pymca_configuration(
    configuration: PyMcaXrfConfiguration,
    energy: Optional[float] = None,
    energy_multiplier: Optional[float] = None,
    mlines: Optional[dict] = None,
    quantification: Optional[Dict[str, float]] = None,
    fast_fitting: bool = False,
) -> None:
    """
    :param configuration: PyMca configuration to modify in-place.
    :param energy: Primary beam energy in keV/
    :param energy_multiplier: Add high primary energy with given multiplier with very low weight.
    :param mlines: Elements (keys) which M line group must be replaced by some M subgroups (values).
    :param quantification: Enable calculation of elemental concentrations when `True` or a dictionary. `None` defaults to `True`.
    :param fast_fitting: Fast fitting means fit all spectra by solving a single linear system of equations.
    """
    set_beam_energy(configuration, energy, energy_multiplier)

    if quantification is False:
        quantification = None
    elif quantification is None or quantification is True:
        quantification = {}
    if quantification is not None:
        enable_quantification(configuration, quantification)

    set_fit_weights(configuration, quantification)

    if fast_fitting:
        force_fast_fitting(configuration)

    if mlines is not None:
        split_mlines(configuration, mlines)

    summary = configuration_summary(configuration, quantification, fast_fitting)
    logger.info("XRF fit configuration summary:\n %s", "\n ".join(summary))
