from datetime import date
import json
from pathlib import Path
from typing import Annotated, Optional, Union
from pydantic import (
    BaseModel,
    BeforeValidator,
    ConfigDict,
    Field,
    SkipValidation,
    field_validator,
    computed_field,
)

from litestar.datastructures import UploadFile

from chitai.schemas.shelf import ShelfRead
from chitai.schemas.author import AuthorRead
from chitai.schemas.tag import TagRead
from chitai.schemas.publisher import PublisherRead


class BookSeriesRead(BaseModel):
    id: int
    title: str


class FileMetadataRead(BaseModel):
    id: int
    path: str
    hash: str
    size: int
    content_type: str

    @computed_field
    @property
    def filename(self) -> str:
        return Path(self.path).name


class BookRead(BaseModel):
    id: int
    library_id: int
    title: str
    subtitle: str | None
    description: str | None
    cover_image: Path | None
    authors: list["AuthorRead"]
    edition: int | None
    publisher: Optional["PublisherRead"]
    published_date: date | None
    identifiers: dict[str, str]
    language: str | None
    pages: int | None
    tags: list["TagRead"]
    lists: list[ShelfRead]
    series: Optional["BookSeriesRead"]
    series_position: str | None
    files: list["FileMetadataRead"]
    progress: Union["BookProgressRead", None] = None

    @field_validator("identifiers", mode="before")
    @classmethod
    def convert_dict(cls, value) -> dict[str, str]:
        if isinstance(value, list):
            return {item.name: item.value for item in value}
        return value


def split_string_and_remove_duplicates(value: list | str) -> list:
    if isinstance(
        value, str
    ):  # If a string was received, split it into individual authors
        value = value.split(",")

    return list(dict.fromkeys(value))  # Remove duplicates, keep insertion order


class BookCreate(BaseModel):
    library_id: int
    title: str | None = None
    subtitle: str | None = None
    description: str | None = None
    authors: Annotated[
        list[str],
        BeforeValidator(split_string_and_remove_duplicates),
    ] = Field(default_factory=list)
    tags: Annotated[
        list[str],
        BeforeValidator(split_string_and_remove_duplicates),
    ] = Field(default_factory=list)
    edition: int | None = None
    publisher: str | None = None
    published_date: date | None = None
    identifiers: dict[str, str] | None = Field(default_factory=dict)
    language: str | None = None
    pages: int | None = None

    series: str | None = None
    series_position: str | None = None
    cover_image: Annotated[UploadFile | None, SkipValidation] = None
    files: Annotated[list[UploadFile], SkipValidation] = Field(default_factory=list)

    @field_validator("identifiers", mode="before")
    @classmethod
    def to_dict(cls, value) -> dict[str, str]:
        if isinstance(value, str):
            return json.loads(value)
        return value or {}

    model_config = ConfigDict(arbitrary_types_allowed=True)


class BooksCreateFromFiles(BaseModel):
    """Upload schema for multiple books with only files."""

    files: Annotated[list[UploadFile], SkipValidation, Field(default_factory=list)]

    model_config = ConfigDict(arbitrary_types_allowed=True)


class BookMetadataUpdate(BaseModel):
    title: str | None = None
    subtitle: str | None = None
    description: str | None = None
    authors: Annotated[
        list[str],
        BeforeValidator(split_string_and_remove_duplicates),
        Field(default_factory=list),
    ]
    tags: Annotated[
        list[str],
        BeforeValidator(split_string_and_remove_duplicates),
        Field(default_factory=list),
    ]
    edition: int | None = None
    publisher: str | None = None
    published_date: date | None = None
    identifiers: dict[str, str] | None = dict()
    language: str | None = None
    pages: int | None = None

    series: str | None = None
    series_position: str | None = None

    @field_validator("identifiers", mode="before")
    @classmethod
    def to_dict(cls, value) -> dict[str, str]:
        if isinstance(value, str):
            return json.loads(value)
        return value or {}

    model_config = ConfigDict(arbitrary_types_allowed=True)

    @field_validator("title", mode="before")
    @classmethod
    def title_not_none(cls, v):
        if v is None:
            raise ValueError("title cannot be explicitly set to None")
        return v


class BookProgressCreate(BaseModel):
    percentage: float
    epub_cfi: str | None = None
    epub_xpointer: str | None = None
    pdf_page: int | None = None
    completed: bool | None = None
    device_type: str | None = None
    device_id: str | None = None


class BookProgressRead(BaseModel):
    percentage: float
    epub_cfi: str | None = None
    epub_xpointer: str | None = None
    pdf_page: int | None = None
    completed: bool | None = False
    device_type: str | None = None
    device_id: str | None = None
