import dataclasses
import uuid
from dataclasses import dataclass
from typing import Literal, NotRequired, TypedDict, Union

from app.common.data.types import (
    GroupDisplayOptions,
    QuestionDataOptions,
    QuestionDataType,
    QuestionPresentationOptions,
)
from app.common.expressions import EvaluatableExpression, ExpressionContext


@dataclass
class E2ETestUser:
    email_address: str


@dataclass
class GuidanceText:
    heading: str
    body_heading: str
    body_link_text: str
    body_link_url: str
    body_ul_items: list[str]
    body_ol_items: list[str]


@dataclass
class E2ETestUserConfig:
    user_id: str
    email: str
    expected_login_url_pattern: str


@dataclasses.dataclass
class QuestionResponse:
    answer: str | list[str]
    error_message: str | None = None
    check_your_answers_text: str | None = None
    expect_group_validation_error: bool = False


@dataclasses.dataclass
class TextFieldWithData:
    prefix: str
    data_reference: DataReferenceConfig


@dataclasses.dataclass
class DataReferenceConfig:
    data_source: ExpressionContext.ContextSources | Literal["THIS_QUESTION"]
    question_text: str | None = None
    section_text: str | None = None
    collection_text: str | None = None
    data_set_name: str | None = None
    column_name: str | None = None


@dataclasses.dataclass
class E2EManagedExpression:
    evaluatable_expression: EvaluatableExpression
    conditional_on: DataReferenceConfig | None = None
    # Note this assumes you're only referencing one question's answer in an expression, if two are needed for
    # Between/BetweenDates then this will need updating
    context_source: DataReferenceConfig | None = None
    # TODO this is so that we can reference multiple fields in an expression.
    #  Need to refactor this class so we aren't duplicating where we put references
    expression_references: dict[str, DataReferenceConfig] | None = dataclasses.field(default_factory=dict)


class ReportDict(TypedDict):
    name: str
    id: uuid.UUID | None
    sections: list[SectionDict]
    require_certification: bool
    allow_multi_submissions: bool


class SectionDict(TypedDict):
    name: str
    components: list[TQuestionToTest]


class QuestionDict(TypedDict):
    type: QuestionDataType
    text: str
    display_text: str
    text_reference: NotRequired[DataReferenceConfig]
    hint: NotRequired[TextFieldWithData]  # Allows testing the 'Reference data' journey
    display_hint: NotRequired[str]  # For use with the 'Reference data' journey
    answers: list[QuestionResponse]
    choices: NotRequired[list[str]]
    options: NotRequired[QuestionPresentationOptions]
    data_options: NotRequired[QuestionDataOptions]
    guidance: NotRequired[GuidanceText]
    validation: NotRequired[E2EManagedExpression]
    condition: NotRequired[E2EManagedExpression]


class QuestionGroupDict(TypedDict):
    type: Literal["group"]
    text: str
    display_options: GroupDisplayOptions
    guidance: NotRequired[GuidanceText]
    condition: NotRequired[E2EManagedExpression]
    validation: NotRequired[E2EManagedExpression]
    questions: list[TQuestionToTest]
    add_another: NotRequired[bool]


TQuestionToTest = Union[QuestionDict, QuestionGroupDict]
