import datetime

from apiflask import APIBlueprint
from flask import abort

from ...base.schemas import MessageSchema
from ...dependencies import db
from ...examination_schedule.models import TemporaryAvailability, TermOfDefence
from ..query.enrollments import (
    check_the_term_of_defence_not_exists_in_chosen_date_range,
    get_and_check_the_project_supervisors_exists_in_db,
    get_examination_schedule_by_id,
    get_term_of_defence_by_id_and_examination_schedule_id,
    set_new_group_to_term_of_defence,
    validate_enrollments_date,
)
from ..schemas.enrollments import (
    AssignedGroupToTermOfDefenceListSchema,
    TemporaryAvailabilityListSchema,
    TermOfDefenceSchema,
)
from ..schemas.groups import GroupIdSchema
from ..utils import generate_range_dates

bp = APIBlueprint("enrollments", __name__, url_prefix="/enrollments")


@bp.post("/<int:examination_schedule_id>/add")
@bp.input(TermOfDefenceSchema)
@bp.output(MessageSchema)
def create_term_of_defence(examination_schedule_id: int, data: dict) -> dict:
    chairman_of_committee_id = data.pop("chairman_of_committee")
    project_supervisors_ids = data.pop("project_supervisors")
    if chairman_of_committee_id not in project_supervisors_ids:
        abort(400, "Invalid id of chairman committee!")

    ex = get_examination_schedule_by_id(examination_schedule_id)

    project_supervisors = get_and_check_the_project_supervisors_exists_in_db(
        ex.year_group_id, project_supervisors_ids
    )
    validate_enrollments_date(ex.start_date, ex.end_date, ex.duration_time, data)
    check_the_term_of_defence_not_exists_in_chosen_date_range(
        examination_schedule_id, data
    )
    td = TermOfDefence(
        **data,
        examination_schedule_id=examination_schedule_id,
        chairman_of_committee=chairman_of_committee_id
    )
    db.session.add(td)
    db.session.commit()
    for p in project_supervisors:
        td.members_of_committee.append(p)
    db.session.commit()
    return {"message": "Term of defence was created!"}


@bp.post("/<int:examination_schedule_id>/add-term-of-defences/")
@bp.input(TermOfDefenceSchema)
@bp.output(MessageSchema)
def create_many_term_of_defences(examination_schedule_id: int, data: dict) -> dict:
    chairman_of_committee_id = data.pop("chairman_of_committee")
    project_supervisors_ids = data.pop("project_supervisors")
    if chairman_of_committee_id not in project_supervisors_ids:
        abort(400, "Invalid id of chairman committee!")

    ex = get_examination_schedule_by_id(examination_schedule_id)

    project_supervisors = get_and_check_the_project_supervisors_exists_in_db(
        ex.year_group_id, project_supervisors_ids
    )
    validate_enrollments_date(ex.start_date, ex.end_date, ex.duration_time, data)
    check_the_term_of_defence_not_exists_in_chosen_date_range(
        examination_schedule_id, data
    )

    # create many here
    start_date = data.get("start_date")
    end_date = data.get("end_date")
    dates = generate_range_dates(start_date, end_date, ex.duration_time)
    for start_date in dates:
        end_date = start_date + datetime.timedelta(minutes=ex.duration_time)

        td = TermOfDefence(
            start_date=start_date,
            end_date=end_date,
            examination_schedule_id=examination_schedule_id,
            chairman_of_committee=chairman_of_committee_id,
        )
        td.members_of_committee = project_supervisors
        db.session.add(td)
    db.session.commit()
    return {"message": "Term of defences was created!"}


@bp.put("/<int:examination_schedule_id>/update/<int:term_of_defence_id>/")
@bp.input(TermOfDefenceSchema)
@bp.output(MessageSchema)
def update_term_of_defence(
    examination_schedule_id: int, term_of_defence_id: int, data: dict
) -> dict:
    chairman_of_committee_id = data.pop("chairman_of_committee")
    project_supervisors_ids = data.pop("project_supervisors")
    if chairman_of_committee_id not in project_supervisors_ids:
        abort(400, "Invalid id of chairman committee!")

    td_query = TermOfDefence.query.filter(
        TermOfDefence.id == term_of_defence_id,
        TermOfDefence.examination_schedule_id == examination_schedule_id,
    )
    td = td_query.first()
    if td is None:
        abort(404, "Not found term of defence!")

    ex = get_examination_schedule_by_id(examination_schedule_id)

    project_supervisors = get_and_check_the_project_supervisors_exists_in_db(
        ex.year_group_id, project_supervisors_ids
    )
    validate_enrollments_date(ex.start_date, ex.end_date, ex.duration_time, data)
    check_the_term_of_defence_not_exists_in_chosen_date_range(
        examination_schedule_id, data
    )

    td_query.update(data)
    td.members_of_committee = []
    td.chairman_of_committee = chairman_of_committee_id
    db.session.commit()
    for p in project_supervisors:
        td.members_of_committee.append(p)
    db.session.commit()

    return {"message": "Term of defence was updated!"}


@bp.delete("/<int:examination_schedule_id>/delete/<int:term_of_defence_id>/")
@bp.output(MessageSchema)
def delete_term_of_defence(
    examination_schedule_id: int, term_of_defence_id: int
) -> dict:
    td = get_term_of_defence_by_id_and_examination_schedule_id(
        examination_schedule_id, term_of_defence_id
    )
    db.session.delete(td)
    db.session.commit()
    return {"message": "Term of defence was deleted!"}


@bp.get("/<int:examination_schedule_id>/term-of-defences/")
@bp.output(AssignedGroupToTermOfDefenceListSchema)
def list_of_term_of_defences(examination_schedule_id: int) -> dict:
    get_examination_schedule_by_id(examination_schedule_id)

    td = (
        TermOfDefence.query.join(TermOfDefence.members_of_committee, isouter=True)
        .filter(TermOfDefence.examination_schedule_id == examination_schedule_id)
        .all()
    )
    return {"term_of_defences": td}


@bp.get("/<int:examination_schedule_id>/temporary-availabilities/")
@bp.output(TemporaryAvailabilityListSchema)
def list_of_temporary_availability(examination_schedule_id: int) -> dict:
    get_examination_schedule_by_id(examination_schedule_id)

    td = (
        TemporaryAvailability.query.filter(
            TemporaryAvailability.examination_schedule_id == examination_schedule_id
        )
        .join(TemporaryAvailability.project_supervisor)
        .all()
    )
    return {"temporary_availabilities": td}


@bp.get("/<int:examination_schedule_id>/assigned-group-to-term-of-defences/")
@bp.output(AssignedGroupToTermOfDefenceListSchema)
def list_of_assigned_group_to_term_of_defences(examination_schedule_id: int) -> dict:
    get_examination_schedule_by_id(examination_schedule_id)

    td = (
        TermOfDefence.query.join(TermOfDefence.members_of_committee, isouter=True)
        .join(TermOfDefence.group)
        .filter(TermOfDefence.examination_schedule_id == examination_schedule_id)
        .filter(TermOfDefence.group_id.isnot(None))
        .all()
    )
    return {"term_of_defences": td}


@bp.post(
    "/<int:examination_schedule_id>/term-of-defence/<int:term_of_defence_id>/group/"
)
@bp.input(GroupIdSchema)
@bp.output(MessageSchema)
def add_group_to_term_of_defence(
    examination_schedule_id: int, term_of_defence_id: int, data: dict
) -> dict:
    set_new_group_to_term_of_defence(
        examination_schedule_id, term_of_defence_id, data.get("group_id")
    )
    db.session.commit()
    return {"message": "Group was added to term of defences!"}


@bp.delete(
    "/<int:examination_schedule_id>/term-of-defence/<int:term_of_defence_id>/group/"
)
@bp.output(MessageSchema)
def delete_group_to_term_of_defence(
    examination_schedule_id: int, term_of_defence_id: int
) -> dict:
    td = get_term_of_defence_by_id_and_examination_schedule_id(
        examination_schedule_id, term_of_defence_id
    )
    td.group_id = None
    db.session.commit()
    return {"message": "Group was deleted from term of defences!"}


@bp.put(
    "/<int:examination_schedule_id>/term-of-defence/<int:term_of_defence_id>/group/"
)
@bp.input(GroupIdSchema)
@bp.output(MessageSchema)
def update_group_for_term_of_defence(
    examination_schedule_id: int, term_of_defence_id: int, data: dict
) -> dict:
    set_new_group_to_term_of_defence(
        examination_schedule_id, term_of_defence_id, data.get("group_id")
    )
    db.session.commit()
    return {"message": "Group for term of defence was updated!"}