from __future__ import annotations

from dataclasses import dataclass, field
from datetime import date
from enum import Enum
from numbers import Number
from pathlib import Path

from serieux import Field, Model
from serieux.ctx import Context


@dataclass
class Tree:
    left: Tree | Number
    right: Tree | Number


@dataclass
class Elf:
    name: str
    birthdate: date
    favorite_color: str


@dataclass
class Citizen:
    name: str
    birthyear: int
    hometown: str


@dataclass
class Country:
    languages: list[str]
    capital: str
    population: int
    citizens: list[Citizen]


@dataclass
class World:
    countries: dict[str, Country]


@dataclass
class Point:
    x: int
    y: int


@dataclass
class Point3D(Point):
    z: int


# This one is not a dataclass and useful error messages should point that out
class Pointato:
    x: int
    y: int


class Color(str, Enum):
    RED = "red"
    GREEN = "green"
    BLUE = "blue"


class Level(Enum):
    HI = 2
    MED = 1
    LO = 0


@dataclass
class Pig:
    # How pink the pig is
    pinkness: float

    weight: float
    """Weight of the pig, in kilograms"""

    # Is the pig...
    # truly...
    beautiful: bool = True  # ...beautiful?


@dataclass
class Defaults:
    name: str
    aliases: list[str] = field(default_factory=list)
    cool: bool = field(default=False, kw_only=True)


@dataclass
class Player:
    first: str
    last: str
    batting: float


@dataclass
class Team:
    name: str
    players: list[Player]


@dataclass(frozen=True)
class Job:
    # Name of the job
    title: str
    # How much it pays, in dollars
    yearly_pay: float


@dataclass
class Worker:
    name: str
    job: Job = None


DID = dict[str, "int | DID"]


@dataclass
class DIDHolder:
    did: DID


ListTree = list["Point | ListTree"]


@dataclass
class LTHolder:
    lt: ListTree


class DotDict(dict):
    def __getattr__(self, attr):
        return self[attr]


@dataclass
class Character:
    name: str
    age: int
    occupation: str
    backstory: str

    class SerieuxConfig:
        allow_extras = True


@dataclass(kw_only=True)
class Car:
    horsepower: int


@dataclass
class IdentifiedCar(Car):
    id: int


@dataclass
class File:
    path: Path

    # [serieux: ignore]
    fd: object = None

    def __post_init__(self):
        self.fd = open(self.path, "r")


@dataclass
class Thingies:
    things: list[str]

    @classmethod
    def serieux_model(cls, call_next):
        return Model(
            original_type=cls,
            element_field=Field(type=str),
            from_list=cls,
            to_list=lambda x: x.things,
        )


class PrefixContext(Context):
    prefix: str


@dataclass
class ContextSwitched:
    word: str

    @classmethod
    def serieux_serialize(cls, obj, ctx, call_next):
        if isinstance(ctx, PrefixContext):
            return {"word": f"{ctx.prefix}{obj.word}"}
        else:
            return {"word": obj.word}

    @classmethod
    def serieux_deserialize(cls, obj, ctx, call_next):
        word = obj["word"]
        if isinstance(ctx, PrefixContext) and word.startswith(ctx.prefix):
            word = word[len(ctx.prefix) :]
        return cls(word=word)

    @classmethod
    def serieux_schema(cls, ctx, call_next):
        if isinstance(ctx, PrefixContext):
            word_schema = {"type": "string", "pattern": f"^{ctx.prefix}.*"}
        else:
            word_schema = {"type": "string"}
        return {"type": "object", "properties": {"word": word_schema}}
