from __future__ import annotations  # TODO: Remove if python requirement is >= 3.10

from datetime import datetime
from typing import Optional, Union

from sqlalchemy import UniqueConstraint

from flaschengeist.models import ModelSerializeMixin, UtcDateTime, Serial
from flaschengeist.models.user import User
from flaschengeist.database import db

#########
# Types #
#########

_table_prefix_ = "events_"


class EventType(db.Model, ModelSerializeMixin):
    __tablename__ = _table_prefix_ + "event_type"
    id: int = db.Column(Serial, primary_key=True)
    name: str = db.Column(db.String(30), nullable=False, unique=True)


class JobType(db.Model, ModelSerializeMixin):
    __tablename__ = _table_prefix_ + "job_type"
    id: int = db.Column(Serial, primary_key=True)
    name: str = db.Column(db.String(30), nullable=False, unique=True)


########
# Jobs #
########


class Service(db.Model, ModelSerializeMixin):
    __tablename__ = _table_prefix_ + "service"
    userid: str = ""
    is_backup: bool = db.Column(db.Boolean, default=False)
    value: float = db.Column(db.Numeric(precision=3, scale=2, asdecimal=False), nullable=False)

    _job_id = db.Column("job_id", Serial, db.ForeignKey(f"{_table_prefix_}job.id"), nullable=False, primary_key=True)
    _user_id = db.Column("user_id", Serial, db.ForeignKey("user.id"), nullable=False, primary_key=True)

    user_: User = db.relationship("User")
    job_: Job = db.relationship("Job")

    @property
    def userid(self):
        return self.user_.userid


class Job(db.Model, ModelSerializeMixin):
    __tablename__ = _table_prefix_ + "job"
    _type_id = db.Column("type_id", Serial, db.ForeignKey(f"{_table_prefix_}job_type.id"), nullable=False)

    id: int = db.Column(Serial, primary_key=True)
    start: datetime = db.Column(UtcDateTime, nullable=False)
    end: Optional[datetime] = db.Column(UtcDateTime)
    type: Union[JobType, int] = db.relationship("JobType")
    comment: Optional[str] = db.Column(db.String(256))
    services: list[Service] = db.relationship("Service", back_populates="job_")
    required_services: float = db.Column(db.Numeric(precision=4, scale=2, asdecimal=False), nullable=False)

    event_ = db.relationship("Event", back_populates="jobs")
    event_id_ = db.Column("event_id", Serial, db.ForeignKey(f"{_table_prefix_}event.id"), nullable=False)

    __table_args__ = (UniqueConstraint("type_id", "start", "event_id", name="_type_start_uc"),)


##########
# Events #
##########
class Event(db.Model, ModelSerializeMixin):
    """Model for an Event"""

    __tablename__ = _table_prefix_ + "event"
    id: int = db.Column(Serial, primary_key=True)
    start: datetime = db.Column(UtcDateTime, nullable=False)
    end: Optional[datetime] = db.Column(UtcDateTime)
    name: Optional[str] = db.Column(db.String(255))
    description: Optional[str] = db.Column(db.String(512))
    type: Union[EventType, int] = db.relationship("EventType")
    is_template: bool = db.Column(db.Boolean, default=False)
    jobs: list[Job] = db.relationship(
        "Job", back_populates="event_", cascade="all,delete,delete-orphan", order_by="[Job.start, Job.end]"
    )
    # Protected for internal use
    _type_id = db.Column(
        "type_id", Serial, db.ForeignKey(f"{_table_prefix_}event_type.id", ondelete="CASCADE"), nullable=False
    )


class Invite(db.Model, ModelSerializeMixin):
    __tablename__ = _table_prefix_ + "invite"

    id: int = db.Column(Serial, primary_key=True)
    job_id: int = db.Column(Serial, db.ForeignKey(_table_prefix_ + "job.id"), nullable=False)
    # Dummy properties for API export
    invitee_id: str = None
    sender_id: str = None
    # Not exported properties for backend use
    invitee_: User = db.relationship("User", foreign_keys="Invite._invitee_id")
    sender_: User = db.relationship("User", foreign_keys="Invite._sender_id")
    # Protected properties needed for internal use
    _invitee_id = db.Column("invitee_id", Serial, db.ForeignKey("user.id"), nullable=False)
    _sender_id = db.Column("sender_id", Serial, db.ForeignKey("user.id"), nullable=False)

    @property
    def invitee_id(self):
        return self.invitee_.userid

    @property
    def sender_id(self):
        return self.sender_.userid