from typing import TYPE_CHECKING, List, Optional, Type
from uuid import UUID

from sqlalchemy import event
from sqlalchemy.schema import Column, ForeignKey, UniqueConstraint
from sqlmodel import Field, Relationship
from sqlmodel.sql.sqltypes import GUID

from joj.horse.models.base import DomainURLORMModel, url_pre_save
from joj.horse.models.link_tables import ProblemProblemSetLink
from joj.horse.schemas.problem import ProblemDetail, WithLatestRecordType
from joj.horse.services.db import db_session

if TYPE_CHECKING:
    from joj.horse.models import (
        Domain,
        ProblemConfig,
        ProblemGroup,
        ProblemSet,
        Record,
        User,
    )


class Problem(DomainURLORMModel, ProblemDetail, table=True):  # type: ignore[call-arg]
    __tablename__ = "problems"
    __table_args__ = (UniqueConstraint("domain_id", "url"),)

    domain_id: UUID = Field(
        sa_column=Column(
            GUID, ForeignKey("domains.id", ondelete="CASCADE"), nullable=False
        )
    )
    domain: "Domain" = Relationship(back_populates="problems")

    owner_id: Optional[UUID] = Field(
        sa_column=Column(
            GUID, ForeignKey("users.id", ondelete="SET NULL"), nullable=True
        )
    )
    owner: Optional["User"] = Relationship(back_populates="owned_problems")

    problem_group_id: Optional[UUID] = Field(
        sa_column=Column(
            GUID, ForeignKey("problem_groups.id", ondelete="SET NULL"), nullable=True
        )
    )
    problem_group: Optional["ProblemGroup"] = Relationship(back_populates="problems")

    problem_sets: List["ProblemSet"] = Relationship(
        back_populates="problems",
        link_model=ProblemProblemSetLink,
    )
    problem_problem_set_links: List[ProblemProblemSetLink] = Relationship(
        back_populates="problem",
    )

    records: List["Record"] = Relationship(back_populates="problem")
    problem_configs: List["ProblemConfig"] = Relationship(back_populates="problem")

    @classmethod
    async def get_problems_with_record_states(
        cls,
        result_cls: Type[WithLatestRecordType],
        problem_set_id: Optional[UUID],
        problems: List["Problem"],
        user_id: UUID,
    ) -> List[WithLatestRecordType]:
        from joj.horse import models

        problem_ids = [problem.id for problem in problems]
        records = await models.Record.get_user_latest_records(
            problem_set_id=problem_set_id, problem_ids=problem_ids, user_id=user_id
        )
        problems = [
            result_cls(**problems[i].dict(), latest_record=records[i])
            for i, record in enumerate(records)
        ]
        return problems

    async def get_latest_problem_config(self) -> Optional["ProblemConfig"]:
        from joj.horse import models

        statement = (
            models.ProblemConfig.sql_select()
            .where(models.ProblemConfig.problem_id == self.id)
            .order_by(models.ProblemConfig.created_at.desc())  # type: ignore
            .limit(1)
        )
        async with db_session() as session:
            results = await session.exec(statement)
            return results.one_or_none()


event.listen(Problem, "before_insert", url_pre_save)
event.listen(Problem, "before_update", url_pre_save)

sqlmodel.Relationship sqlalchemy.event.listen sqlalchemy.schema.ForeignKey joj.horse.services.db.db_session joj.horse.models.ProblemConfig.sql_select sqlalchemy.schema.UniqueConstraint joj.horse.models.ProblemConfig.created_at.desc joj.horse.models.Record.get_user_latest_records
[((3154, 3206), 'sqlalchemy.event.listen', 'event.listen', (['Problem', '"""before_insert"""', 'url_pre_save'], {}), "(Problem, 'before_insert', url_pre_save)\n", (3166, 3206), False, 'from sqlalchemy import event\n'), ((3207, 3259), 'sqlalchemy.event.listen', 'event.listen', (['Problem', '"""before_update"""', 'url_pre_save'], {}), "(Problem, 'before_update', url_pre_save)\n", (3219, 3259), False, 'from sqlalchemy import event\n'), ((1029, 1068), 'sqlmodel.Relationship', 'Relationship', ([], {'back_populates': '"""problems"""'}), "(back_populates='problems')\n", (1041, 1068), False, 'from sqlmodel import Field, Relationship\n'), ((1257, 1302), 'sqlmodel.Relationship', 'Relationship', ([], {'back_populates': '"""owned_problems"""'}), "(back_populates='owned_problems')\n", (1269, 1302), False, 'from sqlmodel import Field, Relationship\n'), ((1524, 1563), 'sqlmodel.Relationship', 'Relationship', ([], {'back_populates': '"""problems"""'}), "(back_populates='problems')\n", (1536, 1563), False, 'from sqlmodel import Field, Relationship\n'), ((1604, 1677), 'sqlmodel.Relationship', 'Relationship', ([], {'back_populates': '"""problems"""', 'link_model': 'ProblemProblemSetLink'}), "(back_populates='problems', link_model=ProblemProblemSetLink)\n", (1616, 1677), False, 'from sqlmodel import Field, Relationship\n'), ((1762, 1800), 'sqlmodel.Relationship', 'Relationship', ([], {'back_populates': '"""problem"""'}), "(back_populates='problem')\n", (1774, 1800), False, 'from sqlmodel import Field, Relationship\n'), ((1847, 1885), 'sqlmodel.Relationship', 'Relationship', ([], {'back_populates': '"""problem"""'}), "(back_populates='problem')\n", (1859, 1885), False, 'from sqlmodel import Field, Relationship\n'), ((1931, 1969), 'sqlmodel.Relationship', 'Relationship', ([], {'back_populates': '"""problem"""'}), "(back_populates='problem')\n", (1943, 1969), False, 'from sqlmodel import Field, Relationship\n'), ((816, 852), 'sqlalchemy.schema.UniqueConstraint', 'UniqueConstraint', (['"""domain_id"""', '"""url"""'], {}), "('domain_id', 'url')\n", (832, 852), False, 'from sqlalchemy.schema import Column, ForeignKey, UniqueConstraint\n'), ((2352, 2466), 'joj.horse.models.Record.get_user_latest_records', 'models.Record.get_user_latest_records', ([], {'problem_set_id': 'problem_set_id', 'problem_ids': 'problem_ids', 'user_id': 'user_id'}), '(problem_set_id=problem_set_id,\n    problem_ids=problem_ids, user_id=user_id)\n', (2389, 2466), False, 'from joj.horse import models\n'), ((3034, 3046), 'joj.horse.services.db.db_session', 'db_session', ([], {}), '()\n', (3044, 3046), False, 'from joj.horse.services.db import db_session\n'), ((929, 973), 'sqlalchemy.schema.ForeignKey', 'ForeignKey', (['"""domains.id"""'], {'ondelete': '"""CASCADE"""'}), "('domains.id', ondelete='CASCADE')\n", (939, 973), False, 'from sqlalchemy.schema import Column, ForeignKey, UniqueConstraint\n'), ((1152, 1195), 'sqlalchemy.schema.ForeignKey', 'ForeignKey', (['"""users.id"""'], {'ondelete': '"""SET NULL"""'}), "('users.id', ondelete='SET NULL')\n", (1162, 1195), False, 'from sqlalchemy.schema import Column, ForeignKey, UniqueConstraint\n'), ((1394, 1446), 'sqlalchemy.schema.ForeignKey', 'ForeignKey', (['"""problem_groups.id"""'], {'ondelete': '"""SET NULL"""'}), "('problem_groups.id', ondelete='SET NULL')\n", (1404, 1446), False, 'from sqlalchemy.schema import Column, ForeignKey, UniqueConstraint\n'), ((2927, 2965), 'joj.horse.models.ProblemConfig.created_at.desc', 'models.ProblemConfig.created_at.desc', ([], {}), '()\n', (2963, 2965), False, 'from joj.horse import models\n'), ((2808, 2841), 'joj.horse.models.ProblemConfig.sql_select', 'models.ProblemConfig.sql_select', ([], {}), '()\n', (2839, 2841), False, 'from joj.horse import models\n')]