import os
from datetime import datetime
from pathlib import Path
from typing import Any
from typing import List
from typing import Optional
from typing import Union

import numpy

os.environ["HDF5_USE_FILE_LOCKING"] = "FALSE"

import h5py  # noqa: E402
from ewoksdata.data.bliss import get_data  # noqa: E402


def get_measurement_data(
    scan_no: Union[str, int],
    filename: Union[str, Path],
    name: str,
    retry_timeout: Optional[int] = None,
    retry_period: Optional[int] = None,
) -> numpy.ndarray:
    return get_data(
        f"{filename}::/{scan_no}.1/measurement/{name}",
        retry_timeout=retry_timeout,
        retry_period=retry_period,
    )


def get_scan_motor(
    scan_no: int,
    filename: Union[str, Path],
    motor_name: str,
    prefix: str = "",
    retry_timeout: Optional[int] = None,
    retry_period: Optional[int] = None,
) -> numpy.ndarray:
    h5path = f"{filename}::/{prefix}{scan_no}.1/instrument/positioners/{motor_name}"
    motor_pos = get_data(h5path, retry_timeout=retry_timeout, retry_period=retry_period)

    return motor_pos


def get_scan_start_time(
    scan_no: int,
    filename: Union[str, Path],
    prefix: str = "",
    retry_timeout: Optional[int] = None,
    retry_period: Optional[int] = None,
) -> float:
    h5path = f"{filename}::/{prefix}{scan_no}.1/start_time"

    start_time = get_data(
        h5path, retry_timeout=retry_timeout, retry_period=retry_period
    )
    epoch = datetime.strptime(start_time.decode(), "%Y-%m-%dT%H:%M:%S.%f%z").timestamp()
    return epoch


def get_scan_title(
    scan_no: int,
    filename: Union[str, Path],
    retry_timeout: Optional[int] = None,
    retry_period: Optional[int] = None,
):
    title = get_data(
        f"{filename}::{scan_no}.1/title",
        retry_timeout=retry_timeout,
        retry_period=retry_period,
    )
    if not isinstance(title, bytes):
        raise TypeError(
            f"{filename}::{scan_no}.1/title is not a string dataset. Got {title} as value."
        )

    return title.decode()


def save_fits(
    filename: Union[str, Path],
    entry_name: str,
    xs: List[numpy.ndarray],
    intensities: List[numpy.ndarray],
    intensity_fits: List[numpy.ndarray],
    labels: List[str],
    offset_x: numpy.ndarray,
    offset_y: numpy.ndarray,
    offset_fit: numpy.ndarray,
    parameters: dict[str, Any],
) -> str:
    with h5py.File(filename, "a") as h5file:
        entry = h5file.create_group(entry_name)
        entry.attrs["NX_class"] = "NXentry"

        cdi_fits = entry.create_group("cdi_fits")
        cdi_fits.attrs["NX_class"] = "NXprocess"

        parameters_grp = cdi_fits.create_group("parameters")
        parameters_grp.attrs["NX_class"] = "NXcollection"

        for k, v in parameters.items():
            if v is not None:
                parameters_grp.create_dataset(k, data=v)

        intensity_fits_grp = cdi_fits.create_group("intensity_fits")
        intensity_fits_grp.attrs["NX_class"] = "NXcollection"
        for i in range(len(xs)):
            x = xs[i]
            label = labels[i]
            intensity = intensities[i]
            fit = intensity_fits[i]
            intensity_fit = intensity_fits_grp.create_group(f"intensity_fit_{i}")
            intensity_fit.attrs["NX_class"] = "NXdata"
            sig = intensity_fit.create_dataset("intensity", data=intensity)
            sig.attrs["long_name"] = label
            intensity_fit.attrs["signal"] = "intensity"
            intensity_fit.create_dataset("fit", data=fit)
            intensity_fit.attrs["auxiliary_signals"] = ["fit"]
            intensity_fit.create_dataset("x", data=x)
            intensity_fit.attrs["axes"] = ["x"]

        offset_fit_grp = cdi_fits.create_group("offset_fit")
        offset_fit_grp.attrs["NX_class"] = "NXdata"
        offset_fit_grp.create_dataset("y", data=offset_y)
        offset_fit_grp.attrs["signal"] = "y"
        offset_fit_grp.create_dataset("fit", data=offset_fit)
        offset_fit_grp.attrs["auxiliary_signals"] = ["fit"]
        offset_fit_grp.create_dataset("x", data=offset_x)
        offset_fit_grp.attrs["axes"] = ["x"]

        cdi_fits.attrs["default"] = "offset_fit"
        entry.attrs["default"] = "cdi_fits"
        h5file.attrs["default"] = entry_name

    return str(filename)
