import datetime

from sqlalchemy import (
    Column,
    ForeignKey,
    types,
    UniqueConstraint,
)
from sqlalchemy.orm import relationship
from .base import BaseModel, TimestampedModelMixin
from watcher import enums


class SchedulesGroup(TimestampedModelMixin, BaseModel):
    __tablename__ = 'schedules_group'

    id = Column(types.Integer, primary_key=True, index=True)
    slug = Column(types.String, unique=True, nullable=False)
    name = Column(types.String, nullable=False, )

    timeout_between_shifts = Column(
        types.Interval,
        nullable=False,
        default=datetime.timedelta(days=0),
        info={
            'verbose_name': 'Число дней перерыва между сменами для дежурного'
        },
    )

    # технические поля
    autogenerated = Column(types.Boolean, default=False)
    people_allocation_error = Column(
        types.JSON,
        nullable=True,
        info={
            'verbose_name': 'Ошибка распределения людей'
        },
    )
    last_people_allocation_at = Column(types.DateTime(timezone=True), nullable=True)
    responsibles = relationship('Staff', secondary='schedule_group_responsible', backref='group_responsibles_in')


class ScheduleShowInServices(TimestampedModelMixin, BaseModel):
    __tablename__ = 'schedule_show_in_services'
    id = Column(types.Integer, primary_key=True, index=True)
    service_id = Column(types.Integer, ForeignKey('services_service.id', ondelete='CASCADE'), nullable=False,)
    service = relationship('Service', overlaps='show_in_services,related_schedules')
    schedule_id = Column(types.Integer, ForeignKey('schedule.id', ondelete='CASCADE'), nullable=False,)
    schedule = relationship('Schedule', overlaps='show_in_services,related_schedules')


class ScheduleGroupResponsible(TimestampedModelMixin, BaseModel):
    __tablename__ = 'schedule_group_responsible'

    id = Column(types.Integer, primary_key=True)
    responsible_id = Column(types.Integer, ForeignKey('intranet_staff.id'),)
    responsible = relationship('Staff', overlaps='responsibles,group_responsibles_in')
    schedule_group_id = Column(types.Integer, ForeignKey('schedules_group.id', ondelete='CASCADE'), )
    schedule_group = relationship('SchedulesGroup', overlaps='responsibles,group_responsibles_in')


class ScheduleResponsible(TimestampedModelMixin, BaseModel):
    __tablename__ = 'schedule_responsible'

    id = Column(types.Integer, primary_key=True)
    responsible_id = Column(types.Integer, ForeignKey('intranet_staff.id'),)
    responsible = relationship('Staff', overlaps='responsibles,schedule_responsibles_in')
    schedule_id = Column(types.Integer, ForeignKey('schedule.id', ondelete='CASCADE'), )
    schedule = relationship('Schedule', overlaps='responsibles,schedule_responsibles_in')


class Schedule(TimestampedModelMixin, BaseModel):
    __tablename__ = 'schedule'

    id = Column(types.Integer, primary_key=True, index=True)
    slug = Column(types.String, nullable=False)
    name = Column(types.String, nullable=False)
    description = Column(types.String)
    state = Column(
        types.Enum(enums.ScheduleState),
        nullable=False,
        default=enums.ScheduleState.active,
    )
    author_id = Column(types.Integer, ForeignKey('intranet_staff.id'), nullable=False,)
    author = relationship('Staff', foreign_keys=[author_id])
    service_id = Column(types.Integer, ForeignKey('services_service.id'), nullable=False,)
    service = relationship('Service', foreign_keys=[service_id])
    schedules_group_id = Column(types.Integer, ForeignKey('schedules_group.id'), nullable=False, )
    schedules_group = relationship('SchedulesGroup', foreign_keys=[schedules_group_id], backref='schedules')
    is_important = Column(types.Boolean, default=False, nullable=False,)
    threshold_day = Column(
        types.Interval,
        nullable=False,
        default=datetime.timedelta(days=60),
        info={'verbose_name': "на какое количество дней вперёд рассчитывать график"},
    )

    # непосредственно настройки графика
    rotation = Column(
        types.Enum(enums.IntervalRotation),
        nullable=False,
        default=enums.IntervalRotation.default,
    )
    days_for_notify_of_problems = Column(
        types.Interval,
        nullable=True,
        info={
            'verbose_name': 'За сколько дней до начала уведомлять о проблемах в смене'
        },
    )
    days_for_notify_of_begin = Column(
        types.ARRAY(types.Interval),
        nullable=True,
        info={
            'verbose_name': 'За сколько дней до начала уведомлять о начале смены'
        },
    )
    length_of_absences = Column(
        types.Interval,
        nullable=False,
        default=datetime.timedelta(days=0),
        info={
            'verbose_name': 'Длина отсутствий, которые не считать проблемой'
        },
    )
    pin_shifts = Column(
        types.Interval,
        nullable=False,
        default=datetime.timedelta(days=7),
        info={
            'verbose_name': 'За сколько дней до начала фиксировать смены'
        },
    )
    recalculate = Column(
        types.Boolean,
        default=True,
        info={
            'verbose_name': 'График участвует в пересчётах смен и перераспределнии людей'
        },
    )
    recalculation_in_process = Column(types.Boolean, default=False)
    show_in_services = relationship('Service', secondary='schedule_show_in_services', backref='related_schedules')
    responsibles = relationship('Staff', secondary='schedule_responsible', backref='schedule_responsibles_in')
    actual_problems = relationship(
        'Problem',
        primaryjoin='Schedule.id==Shift.schedule_id',
        secondary='shift',
        secondaryjoin='and_(Problem.shift_id==Shift.id, Problem.status.in_(["new", "reported"]))',
        viewonly=True,
    )
    days_before_vacation = Column(
        types.Interval,
        nullable=False,
        default=datetime.timedelta(days=0),
        info={
            'verbose_name': 'Число дней перед отпуском, на которые не будут ставиться дежурства'
        },
    )
    days_after_vacation = Column(
        types.Interval,
        nullable=False,
        default=datetime.timedelta(days=0),
        info={
            'verbose_name': 'Число дней после отпуска, на которые не будут ставиться дежурства'
        },
    )
    # технические поля
    deleted_at = Column(types.DateTime(timezone=True),)
    deleted_by_id = Column(types.Integer, ForeignKey('intranet_staff.id'),)
    deleted_by = relationship('Staff', foreign_keys=[deleted_by_id])

    shifts_boundary_recalculation_error = Column(
        types.JSON,
        nullable=True,
        info={
            'verbose_name': 'Ошибка в результате пересчёта границ смен'
        },
    )
    stopped_at = Column(types.DateTime(timezone=True),)
    stopped_by_id = Column(types.Integer, ForeignKey('intranet_staff.id'),)
    stopped_by = relationship('Staff', foreign_keys=[stopped_by_id])

    __table_args__ = (
        UniqueConstraint('service_id', 'slug', name='uniq_service_schedule'),
    )
