from apiflask import APIBlueprint
from flask import abort

from ...base.schemas import MessageSchema
from ...base.utils import paginate_models
from ...dependencies import db
from ...project_supervisor.models import ProjectSupervisor
from ...students.models import Group, ProjectGradeSheet, Student, YearGroup
from ..schemas.groups import (
    GroupCreateSchema,
    GroupEditSchema,
    GroupQuerySchema,
    GroupsPaginationSchema,
)
from ..schemas.students import DetailGroupSchema
from ..utils import attach_points_for_first_and_second_term_to_group_models

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


@bp.get("/<int:year_group_id>/")
@bp.input(GroupQuerySchema, location="query")
@bp.output(GroupsPaginationSchema)
def list_groups(year_group_id: int, query: dict) -> dict:
    search_name = query.get("name")
    page = query.get("page")
    per_page = query.get("per_page")

    groups_query = Group.search_by_name(year_group_id, search_name)
    data = paginate_models(page, groups_query, per_page)

    items = data["items"]
    attach_points_for_first_and_second_term_to_group_models(items)

    return {"groups": items, "max_pages": data["max_pages"]}


@bp.post("/<int:year_group_id>/")
@bp.input(GroupCreateSchema)
@bp.output(MessageSchema, status_code=201)
def create_group(year_group_id: int, data: dict) -> dict:
    name = data["name"]
    students_ids = data["students"]
    project_supervisor_id = data["project_supervisor_id"]

    yg = YearGroup.query.filter(YearGroup.id == year_group_id).first()
    if yg is None:
        abort(404, "Not found year group!")

    project_supervisor = ProjectSupervisor.query.filter_by(
        id=project_supervisor_id
    ).first()

    if project_supervisor is None:
        abort(404, "Not found project supervisor!")

    group = Group(
        name=name,
        project_supervisor_id=project_supervisor_id,
        year_group_id=year_group_id,
    )

    students_without_groups = (
        db.session.query(Student, Group)
        .join(Group, Student.groups)
        .filter(Group.year_group_id == year_group_id)
        .filter(db.or_(*[Student.id == st_id for st_id in students_ids]))
        .all()
    )

    if len(students_without_groups) > 0:
        abort(400, "One or more students have already belonged to group!")

    students = db.session.query(Student).filter(Student.id.in_(students_ids)).all()
    if len(students) != len(students_ids):
        abort(404, "Not found students!")

    db.session.add(group)
    db.session.commit()

    for student in students:
        group.students.append(student)
    pgs = ProjectGradeSheet(group_id=group.id)
    db.session.add(pgs)
    db.session.commit()

    return {"message": "Group was created!"}


@bp.get("/<int:group_id>/detail/")
@bp.output(DetailGroupSchema)
def detail_group(group_id: int) -> Group:
    group = Group.query.filter_by(id=group_id).first()
    if group is None:
        abort(404, "Not found group!")
    return group


@bp.delete("/<int:group_id>/")
@bp.output(MessageSchema, status_code=202)
def delete_group(group_id: int) -> dict:
    group = Group.query.filter_by(id=group_id).first()
    if group is None:
        abort(404, "Not found group!")

    group.students = []
    db.session.delete(group)
    db.session.commit()
    return {"message": "Group was deleted!"}


@bp.put("/<int:group_id>/")
@bp.input(GroupEditSchema)
@bp.output(MessageSchema)
def edit_group(group_id: int, data: dict) -> dict:
    if not data:
        abort(400, "You have passed empty data!")

    group_query = Group.query.filter_by(id=group_id)
    group = group_query.first()

    if group is None:
        abort(404, "Not found group!")

    students_ids = data.get("students")
    name = data.get("name")
    project_supervisor_id = data.get("project_supervisor_id")

    if students_ids is not None:
        students = db.session.query(Student).filter(Student.id.in_(students_ids)).all()
        if len(students_ids) != len(students):
            abort(404, "Not found students!")
        group.students = students

    if name is not None:
        group.name = name

    if project_supervisor_id is not None:
        ps = ProjectSupervisor.query.filter(
            ProjectSupervisor.id == project_supervisor_id
        ).first()
        if ps is None:
            abort(404, "Not found project supervisor!")
        group.project_supervisor_id = project_supervisor_id

    db.session.commit()
    return {"message": "Group was updated!"}