"""GNIMBUS specific data classes; REMEMBER to add SER/DES into aggregator!"""

import json

from pydantic import ValidationError
from sqlmodel import JSON, Column, Field, SQLModel, TypeDecorator

from desdeo.api.models.gdm.gdm_base import (
    BaseGroupInfoContainer,
    BooleanDictTypeDecorator,
    ReferencePoint,
    ReferencePointDictType,
)
from desdeo.api.models.generic_states import SolutionReference, SolutionReferenceLite
from desdeo.tools import SolverResults


class SolverResultType(TypeDecorator):
    """Not sure why the solver results wouldn't serialize but this should do the trick."""

    impl = JSON

    def process_bind_param(self, value, dialect):
        """Turns solver results into json."""
        if isinstance(value, list):
            solver_list = []
            for item in value:
                if isinstance(item, SolverResults):
                    solver_list.append(item.model_dump_json())
            return json.dumps(solver_list)
        return None

    def process_result_value(self, value, dialect):
        """And back."""
        if value is not None:
            value_list = json.loads(value)
            solver_list: SolverResults = []
            for item in value_list:
                item_dict = json.loads(item)
                try:
                    solver_list.append(SolverResults.model_validate(item_dict))
                except ValidationError as e:
                    print(f"Validation error when deserializing SolverResults: {e}")
                    continue
            return solver_list
        return None


class GNIMBUSSwitchPhaseRequest(SQLModel):
    """A request for a certain phase. Comes from the group owner/analyst."""

    group_id: int
    new_phase: str


class GNIMBUSSwitchPhaseResponse(SQLModel):
    """A response for the above request."""

    old_phase: str
    new_phase: str


class OptimizationPreference(BaseGroupInfoContainer):
    """A structure for storing optimization preferences. See GNIMBUS for details."""

    method: str = "optimization"
    phase: str = Field(default="learning")
    set_preferences: dict[int, ReferencePoint] = Field(sa_column=Column(ReferencePointDictType))


class VotingPreference(BaseGroupInfoContainer):
    """A structure for storing voting preferences."""

    method: str = "voting"
    set_preferences: dict[int, int] = Field(
        sa_column=Column(JSON)
    )  # A user votes for an index from the results (or something)


class EndProcessPreference(BaseGroupInfoContainer):
    """A structure for storing info on whether everyone is happy to end the gnimbus process."""

    method: str = "end"
    success: bool | None = Field()

    # We check if everyone agrees to stop.
    set_preferences: dict[int, bool] = Field(sa_column=Column(BooleanDictTypeDecorator))


class GNIMBUSResultResponse(SQLModel):
    """The response for getting GNIMBUS results. NOTE: OBSOLETE!"""

    method: str
    phase: str
    preferences: VotingPreference | OptimizationPreference
    common_results: list[SolutionReference]
    user_results: list[SolutionReference]
    personal_result_index: int | None


class FullIteration(SQLModel):
    """A full iteration item containing results from a complete or incomplete iteration.

    This is a format to send information to the user interface.
    """

    phase: str = Field(
        description="The phase of the iteration."
    )
    optimization_preferences: OptimizationPreference | None = Field(
        description="The preferences related to the optimization stage of the full iteration."
    )
    voting_preferences: VotingPreference | EndProcessPreference | None = Field(
        description="The preferences related to the voting phase of the iteration. \
            either actual votes or a vote to see whether to just continue."
    )
    starting_result: SolutionReferenceLite | None = Field(
        description="The starting result of the optimization process. Fetched from the previous \
            iteration's final result."
    )
    common_results: list[SolutionReferenceLite] = Field(
        description="The common results (1 to 4) generated by gnimbus."
    )
    user_results: list[SolutionReferenceLite] = Field(
        description="The user specific results generated by gnimbus in phases learning and crp."
    )
    personal_result_index: int | None = Field(
        description="The user result index of requester."
    )
    final_result: SolutionReferenceLite | None = Field(
        description="The final result after voting."
    )


class GNIMBUSAllIterationsResponse(SQLModel):
    """The response model for getting all found solutions among others."""

    all_full_iterations: list[FullIteration]
