from sqlalchemy import (
    Column,
    ForeignKey,
    types,
)
from sqlalchemy import text
from sqlalchemy.orm import backref, relationship

from .base import BaseModel, TimestampedModelMixin
from watcher import enums


class Slot(TimestampedModelMixin, BaseModel):
    __tablename__ = 'slot'

    id = Column(types.Integer, primary_key=True, index=True)
    interval_id = Column(types.Integer, ForeignKey('interval.id', ondelete='CASCADE'), nullable=False,)
    interval = relationship('Interval', backref=backref('slots', remote_side=[interval_id], cascade='all,delete'))

    # непосредственно настройки
    points_per_hour = Column(
        types.Integer,
        nullable=False,
        default=1,
        info={
            'verbose_name': 'Пункты рейтинга за один час',
        }
    )

    role_on_duty_id = Column(
        types.Integer,
        ForeignKey('roles_role.id'),
        info={
            'verbose_name': 'Роль, выдаваемая на дежурстве',
            'description': 'Может быть null. Означает, что во время дежурства не выдаются роли',
        }
    )
    role_on_duty = relationship('Role')
    composition_id = Column(
        types.Integer,
        ForeignKey('composition.id'),
        info={
            'verbose_name': 'Состав, который участвует в ротации слота',
        }
    )
    composition = relationship('Composition', backref='slots')
    show_in_staff = Column(types.Boolean, default=False)
    is_primary = Column(types.Boolean, nullable=False, default=True, server_default=text('true'))

    # технические поля


class Shift(TimestampedModelMixin, BaseModel):
    __tablename__ = 'shift'

    id = Column(types.Integer, primary_key=True, index=True)
    slot_id = Column(types.Integer, ForeignKey('slot.id', ondelete='CASCADE'),)
    slot = relationship('Slot')
    schedule_id = Column(types.Integer, ForeignKey('schedule.id', ondelete='CASCADE'), nullable=False)
    schedule = relationship('Schedule')
    start = Column(types.DateTime(timezone=True), nullable=False,)
    end = Column(types.DateTime(timezone=True), nullable=False,)
    status = Column(
        types.Enum(enums.ShiftStatus),
        nullable=False,
        default=enums.ShiftStatus.scheduled,
    )
    replacement_for_id = Column(
        types.Integer,
        ForeignKey('shift.id'),
        nullable=True,
        info={
            'verbose_name': 'Является заменой для другого шифта',
            'description': 'Этим признаком помечаются неосновные смены.',
        }
    )
    sub_shifts = relationship(
        'Shift',
        backref=backref('replacement_for', remote_side=[id]),
        foreign_keys=[replacement_for_id],
        post_update=True,
    )

    # настройки, поля, которые можно править
    staff_id = Column(types.Integer, ForeignKey('intranet_staff.id'))
    staff = relationship('Staff', foreign_keys=[staff_id])

    approved = Column(
        types.Boolean,
        default=False,
        info={
            'verbose_name': 'Смена подтверждена',
        }
    )
    approved_by_id = Column(types.Integer, ForeignKey('intranet_staff.id'), )
    approved_by = relationship('Staff', foreign_keys=[approved_by_id])
    approved_at = Column(types.DateTime(timezone=True), )

    approved_removed_by_id = Column(types.Integer, ForeignKey('intranet_staff.id'), )
    approved_removed_by = relationship('Staff', foreign_keys=[approved_removed_by_id])
    approved_removed_at = Column(types.DateTime(timezone=True), )

    is_role_requested = Column(types.Boolean, default=False)
    empty = Column(
        types.Boolean,
        default=False,
        info={
            'verbose_name': 'Смена является пустой',
            'description': 'Этим признаком помечаются смены, в которых никто не должен дежурить.',
        }
    )
    predicted_ratings = Column(
        types.JSON,
        nullable=True,
        info={
            'verbose_name': 'Рейтинги людей на момент начала смены',
            'description': 'Ключами являются логины сотрудников, а значениями - предсказанные рейтинги',
        },
        default={},
    )
    is_primary = Column(types.Boolean, nullable=False, default=True)
    gives_rating = Column(types.Boolean, nullable=False, default=True)

    # технические поля
    next_id = Column(
        types.Integer,
        ForeignKey('shift.id'),
        nullable=True,
        info={
            'verbose_name': 'Следующий шифт внутри графика',
            'description': 'Используем для пересчетов распределения людей и прогнозирования рейтинга, '
                           'чтобы не высчитывать каждый раз приоритет и, соответсвенно, последовательность',
        }
    )
    next = relationship(
        'Shift',
        uselist=False,
        foreign_keys=[next_id],
        backref=backref('prev', uselist=False),
        remote_side=[id],
        post_update=True,
    )

    def copy(self, **kwargs):
        """
        Создаём объект с такими же основными параметрами. Можно передать дополнительные.
        В базе новый объект не комитится.
        """
        return Shift(
            start=self.start,
            end=self.end,
            schedule_id=self.schedule_id,
            status=self.status,
            empty=self.empty,
            **kwargs
        )
