from __future__ import annotations

from pydantic import Field, field_serializer, field_validator, ConfigDict

from nxtomo.nxobject.nxdetector import FieldOfView

from nxtomomill.models.utils import filter_str_def, convert_str_to_bool
from ..base.general_section import GeneralSection as _GeneralSectionBase


class GeneralSection(_GeneralSectionBase):
    model_config = ConfigDict(validate_assignment=True, validate_by_name=True)

    input_file: str | None = Field(default=None, description="Input file path.")
    raises_error: bool = Field(
        default=False, description="Raise an error when encountered."
    )
    no_input: bool = Field(
        default=True,
        description="If True, do not ask for user inputs if missing information.",
    )
    single_file: bool = Field(
        default=False, description="Create a single file for all sequences."
    )
    no_master_file: bool = Field(
        default=True, description="Avoid creating the master file."
    )
    field_of_view: FieldOfView | None = Field(
        default=None,
        description="Force output to be a 'Full' or 'Half' acquisition. Else determine from existing metadata",
    )
    create_control_data: bool = Field(
        default=True, description="Generate control/data - filled with machine current."
    )
    check_tomo_n: bool | None = Field(
        default=None,
        description="Check 'tomo_n' metadata once the conversion is done. By default will be done for all except scan build from 'scan_data'.",
    )
    rotation_is_clockwise: bool | None = Field(
        default=None,
        description="Force rotation clockwise or not. If not set will read the information from the bliss-tomo 'tomoconfig' group. Else consider rotation is counterclockwise.",
    )
    mechanical_lr_flip: bool = Field(
        default=False,
        description="Detector image is flipped **left-right** for mechanical reasons that are not propagated with bliss-tomo hdf5 metadata (mirror,..).",
    )
    mechanical_ud_flip: bool = Field(
        default=False,
        description="Detector image is flipped **up-down** for mechanical reasons that are not propagated with bliss-tomo hdf5 metadata (mirror,..).",
    )

    @property
    def request_input(self) -> bool:
        return not self.no_input

    @request_input.setter
    def request_input(self, request: bool):
        self.no_input = request

    @field_validator(
        "check_tomo_n",
        "rotation_is_clockwise",
        mode="before",
    )
    @classmethod
    def cast_general_section_to_bool_or_none(
        cls, value: str | None | bool
    ) -> bool | None:
        if isinstance(value, str):
            value = filter_str_def(value)
        if value in (None, "None", ""):
            return None
        elif value in ("True", True, "1"):
            return True
        elif value in ("False", False, "0"):
            return False
        else:
            raise ValueError

    @field_serializer(
        "check_tomo_n",
        "rotation_is_clockwise",
        when_used="always",
    )
    @classmethod
    def serialize_check_tomo_n(cls, value: None | bool) -> str:
        if value is None:
            return None
        else:
            return value

    @field_validator(
        "field_of_view",
        mode="plain",
    )
    @classmethod
    def cast_to_field_of_view(cls, value: str | FieldOfView) -> FieldOfView | None:
        if value in (None, ""):
            return None
        return FieldOfView(value)

    @field_serializer(
        "field_of_view",
        when_used="always",
    )
    @classmethod
    def serialize_field_of_view(cls, field_of_view: FieldOfView | None) -> str:
        if field_of_view is None:
            return None
        else:
            return field_of_view.value

    @field_validator(
        "input_file",
        mode="before",
    )
    @classmethod
    def cast_general_section_to_str(cls, value: str | None) -> str | None:
        return filter_str_def(value)

    @field_validator(
        "no_input",
        "single_file",
        "no_master_file",
        "create_control_data",
        "mechanical_lr_flip",
        "mechanical_ud_flip",
        mode="before",
    )
    @classmethod
    def cast_general_section_to_bool(cls, value: str | bool | None) -> bool:
        return convert_str_to_bool(value)
