import datetime
import logging

from sqlalchemy.orm import Session, joinedload
from sqlalchemy.orm.query import Query
from typing import Optional

from watcher import enums
from watcher.db import Revision, Schedule
from watcher.logic.exceptions import RecordNotFound
from watcher.logic.timezone import now

logger = logging.getLogger(__name__)


def query_revision_by_schedule(db: Session, schedule_id: int, query: Optional[Query] = None) -> Query:
    if not query:
        query = db.query(Revision)
    return query.filter(Revision.schedule_id == schedule_id)


def query_revisions_by_service(db: Session, service_id: int, query: Optional[Query] = None) -> Query:
    if not query:
        query = db.query(Revision)

    return (
        query
        .join(Schedule, Revision.schedule_id == Schedule.id)
        .filter(Schedule.service_id == service_id)
    )


def query_first_revisions_by_schedules(db: Session, schedules_ids: list[int]) -> Query:
    return db.query(Revision).filter(
        Revision.schedule_id.in_(schedules_ids),
        Revision.state == enums.RevisionState.active,
        ~Revision.prev.has(),
    )


def query_current_revision(db: Session, schedule_id: int) -> Optional[Revision]:
    return db.query(Revision).filter(
        Revision.schedule_id == schedule_id,
        Revision.apply_datetime <= now(),
        Revision.state == enums.RevisionState.active,
    ).order_by(Revision.apply_datetime.desc()).first()


def get_current_revision(db: Session, schedule_id: int) -> Revision:
    revision = query_current_revision(db=db, schedule_id=schedule_id)
    if not revision:
        raise RecordNotFound(message={
            'ru': f'Нет активного объекта Revision для графика id={schedule_id}',
            'en': f'There is no active Revision for the schedule id={schedule_id}',
        })

    return revision


def query_active_revisions(db: Session, schedule_id: int, query: Optional[Query] = None) -> Query:
    if not query:
        query = db.query(Revision)

    return query_revision_by_schedule(
        db=db,
        schedule_id=schedule_id,
        query=query.filter(Revision.state == enums.RevisionState.active)
    )


def query_revisions_for_period(db: Session, schedule_id: int, end: datetime, start: datetime = None) -> Query:
    if start is None:
        start = now()

    return query_active_revisions(
        db=db,
        schedule_id=schedule_id,
        query=db.query(Revision).filter(
            Revision.apply_datetime >= start,
            Revision.apply_datetime <= end,
        )
    ).order_by(Revision.apply_datetime)


def get_revision_by_apply_datetime(db: Session, schedule_id: int, apply_datetime: datetime) -> Optional[Revision]:
    return db.query(Revision).filter(
        Revision.schedule_id == schedule_id,
        Revision.state == enums.RevisionState.active,
        Revision.apply_datetime == apply_datetime
    ).options(
        joinedload(Revision.next, Revision.prev)
    ).first()
