from datetime import datetime

from pydantic import BaseModel, Extra


class File:

    def get_content(self):
        pass


class Resource(BaseModel, frozen=True):

    id: int


_TYPE_TO_PATH: dict = {}

_PATH_TO_TYPES: dict = {}


class FileWrapper(BaseModel, frozen=True):
    content: bytes
    filename: str


class ModelWithFiles(BaseModel, frozen=True):
    model: BaseModel
    files: dict[str, FileWrapper] = {}


def register_types(label: str, types: dict):

    if "create" not in types:
        types["create"] = types["detail"]
    if "list" not in types:
        types["list"] = types["detail"]

    _PATH_TO_TYPES[label] = types

    for t in types.values():
        _TYPE_TO_PATH[t.__name__] = label


def get_path_from_item(resource: BaseModel) -> str:
    return _TYPE_TO_PATH[type(resource).__name__]


def get_path_from_type(item_t) -> str:
    return _TYPE_TO_PATH[item_t.__name__]


def get_type(path: str, action: str):
    return _PATH_TO_TYPES[path][action]


class Identifiable(Resource, frozen=True):
    url: str


class Timestamped(Identifiable, frozen=True):
    created_at: datetime
    updated_at: datetime


class MemberIdentifierBase(BaseModel, frozen=True):
    id_type: str
    value: str


class MemberIdentifierCreate(MemberIdentifierBase, frozen=True):
    pass


class MemberIdentifierDetail(Resource, MemberIdentifierBase, frozen=True):
    pass


class MemberBase(BaseModel, frozen=True):
    username: str
    email: str
    first_name: str
    last_name: str | None = None
    phone: str | None = None


class MemberCreate(MemberBase, frozen=True):
    profile: str | None = None
    identifiers: list[MemberIdentifierCreate] = []

    class Config:
        extra = Extra.forbid


class MemberList(Timestamped, MemberBase, frozen=True):
    organizations: list[str] = []
    profile: str | None = None
    profile_thumbnail: str | None = None
    identifiers: list[MemberIdentifierDetail] = []
    verified: bool

    class Config:
        extra = Extra.forbid


class MemberPreferencesBase(BaseModel):
    notifications: dict[str, str]


class MemberPreferencesCreate(BaseModel):
    pass


class MemberPreferencesDetail(MemberPreferencesBase):
    id: int

    class Config:
        extra = Extra.forbid


class MemberDetail(MemberList, frozen=True):
    permissions: list[str] = []
    preferences: MemberPreferencesDetail | None = None

    class Config:
        extra = Extra.forbid


class GroupBase(BaseModel, frozen=True):
    name: str


class GroupList(Identifiable, GroupBase, frozen=True):

    class Config:
        extra = Extra.forbid


class GroupCreate(GroupBase, frozen=True):
    class Config:
        extra = Extra.forbid


class GroupDetail(Identifiable, GroupBase, frozen=True):
    user_set: list[str]

    class Config:
        extra = Extra.forbid


class NotificationBase(BaseModel, frozen=True):
    message: str
    link: str
    read_time: datetime | None


class NotificationDetail(Timestamped, NotificationBase, frozen=True):
    user: str

    class Config:
        extra = Extra.forbid


class FeedbackBase(BaseModel, frozen=True):
    comments: str


class FeedbackCreate(FeedbackBase, frozen=True):
    class Config:
        extra = Extra.forbid


class FeedbackDetail(Timestamped, FeedbackBase, frozen=True):
    creator: str

    class Config:
        extra = Extra.forbid


class AddressBase(BaseModel, frozen=True):
    line1: str
    line2: str | None = None
    line3: str | None = None
    city: str | None = None
    region: str
    postcode: str | None = None
    country: str


class AddressCreate(AddressBase, frozen=True):
    class Config:
        extra = Extra.forbid


class AddressDetail(AddressBase, frozen=True):
    id: int
    country_name: str
    country_flag: str

    class Config:
        extra = Extra.forbid


class OrganizationBase(BaseModel, frozen=True):
    name: str
    acronym: str | None = None
    description: str | None = None
    website: str | None = None


class OrganizationCreate(OrganizationBase, frozen=True):
    address: AddressCreate
    members: list[str] = []

    class Config:
        extra = Extra.forbid


class OrganizationPublicDetail(Timestamped, OrganizationBase, frozen=True):
    address: AddressDetail

    class Config:
        extra = Extra.forbid


class OrganizationDetail(OrganizationPublicDetail, frozen=True):
    public: bool
    members: list[str] = []

    class Config:
        extra = Extra.forbid


class FormFieldBase(BaseModel, frozen=True):

    label: str
    key: str
    required: bool = False
    description: str | None = ""
    tooltip: str | None = ""
    options: str | None = None
    default: str | None = None
    field_type: str
    order: int = 0


class FormFieldCreate(FormFieldBase, frozen=True):
    template: str | None = None

    class Config:
        extra = Extra.forbid


class FormFieldDetail(Resource, FormFieldBase, frozen=True):
    template: bool | None

    class Config:
        extra = Extra.forbid


class FormGroupBase(BaseModel, frozen=True):
    label: str | None = None
    description: str | None = None
    order: int = 0


class FormGroupCreate(FormGroupBase, frozen=True):
    fields: list[FormFieldCreate] = []

    class Config:
        extra = Extra.forbid


class FormGroupDetail(Resource, FormGroupBase, frozen=True):
    fields: list[FormFieldDetail] = []


class FormBase(BaseModel, frozen=True):
    pass


class FormCreate(FormBase, frozen=True):
    groups: list[FormGroupCreate] = []


class FormDetail(Resource, FormBase, frozen=True):
    groups: list[FormGroupDetail] = []


class FormFieldValueBase(BaseModel, frozen=True):
    value: str
    asset: str | None = None


class FormFieldValueCreate(FormFieldValueBase, frozen=True):
    field: int


class FormFieldValueUpdate(Resource, FormFieldValueBase, frozen=True):
    field: int


class FormFieldValueDetail(Resource, FormFieldValueBase, frozen=True):
    field: FormFieldDetail

    def to_update(self):
        return FormFieldValueUpdate(
            id=self.id, value=self.value, asset=self.asset, field=self.field.id
        )


class PopulatedFormBase(BaseModel, frozen=True):
    pass


class PopulatedFormCreate(PopulatedFormBase, frozen=True):
    values: list[FormFieldValueCreate] = []


class PopulatedFormUpdate(PopulatedFormBase, frozen=True):
    values: list[FormFieldValueUpdate] = []


class PopulatedFormDetail(PopulatedFormBase, frozen=True):
    values: list[FormFieldValueDetail] = []

    def to_update(self):
        return PopulatedFormUpdate(values=[v.to_update() for v in self.values])


register_types(
    "members", {"create": MemberCreate, "detail": MemberDetail, "list": MemberList}
)
register_types(
    "groups", {"detail": GroupDetail, "create": GroupCreate, "list": GroupList}
)
register_types("notifications", {"detail": NotificationDetail})
register_types("feedback", {"detail": FeedbackDetail, "create": FeedbackBase})
register_types(
    "organizations",
    {
        "create": OrganizationCreate,
        "detail": OrganizationDetail,
        "detail-public": OrganizationPublicDetail,
        "list": OrganizationPublicDetail,
    },
)
