from flask import Flask, render_template, request, redirect, url_for, flash
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from werkzeug.security import generate_password_hash, check_password_hash
from datetime import datetime

def time_conflict(course1, course2):
    # Check if days overlap
    days1 = set(course1.day.split(","))
    days2 = set(course2.day.split(","))

    if not days1.intersection(days2):
        return False  # no same day

    # Split times
    start1, end1 = course1.time.split("-")
    start2, end2 = course2.time.split("-")

    start1 = datetime.strptime(start1.strip(), "%H:%M")
    end1 = datetime.strptime(end1.strip(), "%H:%M")
    start2 = datetime.strptime(start2.strip(), "%H:%M")
    end2 = datetime.strptime(end2.strip(), "%H:%M")

    # Check overlap
    if start1 < end2 and start2 < end1:
        return True

    return False

app = Flask(__name__)
app.config["SECRET_KEY"] = "supersecretkey"
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///database.db"

db = SQLAlchemy(app)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = "login"

# ------------------ DATABASE MODELS ------------------

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(100), unique=True)
    password = db.Column(db.String(200))


class Course(db.Model):  # Master Course Catalog
    id = db.Column(db.Integer, primary_key=True)
    code = db.Column(db.String(20))
    name = db.Column(db.String(100))
    day = db.Column(db.String(50))
    time = db.Column(db.String(50))
    credits = db.Column(db.Integer)
    semester = db.Column(db.String(20))
    type = db.Column(db.String(20))
    major = db.Column(db.String(50))

    prerequisite = db.Column(db.String(50))  # NEW


class Enrollment(db.Model):  # User Schedule
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    course_id = db.Column(db.Integer, db.ForeignKey('course.id'))
    status = db.Column(db.String(20))
    semester = db.Column(db.String(20))

    grade = db.Column(db.String(2))

    user = db.relationship("User", backref="enrollments")
    course = db.relationship("Course")


# ------------------ LOGIN MANAGER ------------------

@login_manager.user_loader
def load_user(user_id):
    return db.session.get(User, int(user_id))


# ------------------ HOME ------------------

@app.route("/")
@login_required
def home():

    search = request.args.get("search")
    category = request.args.get("category")

    query = Course.query

    # 🔍 SEARCH
    if search:
        query = query.filter(
            (Course.code.ilike(f"%{search}%")) |
            (Course.name.ilike(f"%{search}%"))
        )

    # 📂 CATEGORY FILTER
    if category:
        query = query.filter_by(major=category)

    all_courses = query.all()

    # ---------------- DEGREE PROGRESS ----------------
    completed_courses = Enrollment.query.filter_by(
        user_id=current_user.id,
        status="Completed"
    ).all()

    grade_points = {
        "A": 4.0,
        "B": 3.0,
        "C": 2.0,
        "D": 1.0,
        "F": 0.0
    }

    total_points = 0
    total_credits = 0

    for enrollment in completed_courses:
        if enrollment.grade:
            points = grade_points.get(enrollment.grade, 0)
            credits = enrollment.course.credits

            total_points += points * credits
            total_credits += credits

    gpa = round(total_points / total_credits, 2) if total_credits > 0 else 0

    all_enrollments = Enrollment.query.filter_by(
        user_id=current_user.id
    ).all()

    enrolled_codes = [e.course.code for e in all_enrollments]

    completed_credits = sum(e.course.credits for e in completed_courses)
    total_degree_credits = 120

    # ---------------- COURSE RECOMMENDATIONS ----------------

    completed_codes = [e.course.code for e in completed_courses]

    recommended_courses = []

    current_semester = "Fall"

    for course in all_courses:

        if course.code in enrolled_codes:
            continue

        if course.prerequisite and course.prerequisite not in completed_codes:
            continue

        priority = 1 if course.semester == current_semester else 2

        recommended_courses.append((priority, course))

    # SORT + LIMIT
    recommended_courses.sort(key=lambda x: x[0])
    recommended_courses = [course for _, course in recommended_courses]
    recommended_courses = recommended_courses[:6]

    # ✅ RETURN MUST BE INSIDE FUNCTION
    return render_template(
        "index.html",
        all_courses=all_courses,
        completed_credits=completed_credits,
        total_degree_credits=total_degree_credits,
        recommended_courses=recommended_courses,
        gpa=gpa
    )


#------------------- Schedule --------------------

@app.route("/schedule")
@login_required
def schedule():

    semester = request.args.get("semester")

    from datetime import datetime

    # -------- GET ENROLLMENTS GROUPED BY SEMESTER --------

    if semester and semester != "All":

        my_enrollments = Enrollment.query.filter_by(
            user_id=current_user.id,
            semester=semester
        ).all()

        semesters = {semester: my_enrollments}

    else:

        all_enrollments = Enrollment.query.filter_by(
            user_id=current_user.id
        ).all()

        semesters = {}
        my_enrollments = all_enrollments

        for enrollment in all_enrollments:
            sem = enrollment.semester

            if sem not in semesters:
                semesters[sem] = []

            semesters[sem].append(enrollment)

    total_credits = sum(e.course.credits for e in my_enrollments)

    # -------- BUILD CALENDARS --------

    semester_calendars = {}

    # -------- SORT SEMESTERS CHRONOLOGICALLY --------

    sorted_semesters = sorted(
        semesters.items(),
        key=lambda x: (
            int(x[0][-4:]),              # year
            0 if "Spring" in x[0] else 1   # Spring before Fall
        )
    )

    for sem, enrollments in sorted_semesters:

        calendar_data = []

        for enrollment in enrollments:

            course = enrollment.course
            days = course.day.split(",")

            start_time, end_time = course.time.split("-")

            calendar_data.append({
                "id": enrollment.id,
                "code": course.code,
                "name": course.name,
                "days": [d.strip() for d in days],
                "start": start_time.strip(),
                "end": end_time.strip(),
                "status": enrollment.status,
                "conflict": False
            })

        # -------- DETECT CONFLICTS --------

        for i in range(len(calendar_data)):
            for j in range(i + 1, len(calendar_data)):

                c1 = calendar_data[i]
                c2 = calendar_data[j]

                for day in c1["days"]:
                    if day in c2["days"]:

                        start1 = datetime.strptime(c1["start"], "%H:%M")
                        end1 = datetime.strptime(c1["end"], "%H:%M")

                        start2 = datetime.strptime(c2["start"], "%H:%M")
                        end2 = datetime.strptime(c2["end"], "%H:%M")

                        if start1 < end2 and start2 < end1:
                            c1["conflict"] = True
                            c2["conflict"] = True

        semester_calendars[sem] = calendar_data

    # -------- TIME SLOTS --------

    time_slots = [
        "08:00","09:00","10:00","11:00","12:00",
        "13:00","14:00","15:00","16:00","17:00"
    ]

    # -------- RENDER PAGE --------

    return render_template(
        "schedule.html",
        semester_calendars=semester_calendars,
        time_slots=time_slots,
        datetime=datetime,
        total_credits=total_credits
    )

# ------------------ ADD COURSE TO SCHEDULE ------------------

@app.route("/add/<int:course_id>", methods=["POST"])
@login_required
def add_course(course_id):

    course_to_add = Course.query.get_or_404(course_id)
    semester = request.form.get("semester")

    # ❗ SEMESTER VALIDATION
    # selected_term = "Fall" if "Fall" in semester else "Spring"

    # -------- SEMESTER VALIDATION --------
    if semester.startswith("Fall"):
        selected_term = "Fall"
    elif semester.startswith("Spring"):
        selected_term = "Spring"
    else:
        selected_term = ""

    if course_to_add.semester != selected_term:
        flash(f"{course_to_add.code} is only offered in {course_to_add.semester}.", "danger")
        return redirect(url_for("home"))

    # -------- CREDIT LIMIT CHECK --------
    my_enrollments = Enrollment.query.filter_by(
        user_id=current_user.id,
        semester=semester
    ).all()

    total_credits = 0
    for enrollment in my_enrollments:
        total_credits += enrollment.course.credits

    if total_credits + course_to_add.credits > 16:
        flash("You cannot exceed the 16 credit hour limit.", "danger")
        return redirect(url_for("home"))

    # -------- PREREQUISITE CHECK --------
    if course_to_add.prerequisite:

        prereq_course = Course.query.filter_by(
            code=course_to_add.prerequisite
        ).first()

        prereq_taken = Enrollment.query.filter_by(
            user_id=current_user.id,
            course_id=prereq_course.id
        ).first()

        if not prereq_taken:
            flash(f"You must take {course_to_add.prerequisite} before enrolling in {course_to_add.code}.", "danger")
            return redirect(url_for("home"))

    # -------- ALREADY ENROLLED CHECK --------
    existing = Enrollment.query.filter_by(
        user_id=current_user.id,
        course_id=course_id
    ).first()

    if existing:
        flash("You are already enrolled in this course.", "warning")
        return redirect(url_for("home"))

    # -------- ADD COURSE --------
    new_enrollment = Enrollment(
        user_id=current_user.id,
        course_id=course_id,
        semester=semester,
        status="Planned"
    )

    db.session.add(new_enrollment)
    db.session.commit()

    flash("Course added successfully!", "success")
    return redirect(url_for("schedule"))


# ------------------ REMOVE COURSE ------------------

@app.route("/remove/<int:enrollment_id>")
@login_required
def remove_course(enrollment_id):
    enrollment = Enrollment.query.get_or_404(enrollment_id)
    db.session.delete(enrollment)
    db.session.commit()
    flash("Course removed.", "success")
    return redirect(url_for("schedule"))

# ------------------ UPDATE COURSE STATUS ------------------

@app.route("/complete/<int:enrollment_id>", methods=["POST"])
@login_required
def complete_course(enrollment_id):

    enrollment = Enrollment.query.get_or_404(enrollment_id)

    grade = request.form.get("grade")

    enrollment.status = "Completed"
    enrollment.grade = grade

    db.session.commit()

    flash("Course marked as completed!", "success")

    return redirect(url_for("schedule"))


@app.route("/inprogress/<int:enrollment_id>")
@login_required
def mark_inprogress(enrollment_id):

    enrollment = Enrollment.query.get_or_404(enrollment_id)

    if enrollment.user_id != current_user.id:
        flash("Unauthorized action.", "danger")
        return redirect(url_for("schedule"))

    enrollment.status = "In Progress"
    db.session.commit()

    flash("Course marked as in progress.", "success")
    return redirect(url_for("schedule"))

# ------------------ AUTH ------------------

@app.route("/register", methods=["GET", "POST"])
def register():
    if request.method == "POST":
        username = request.form["username"]
        password = request.form["password"]

        existing_user = User.query.filter_by(username=username).first()

        if existing_user:
            flash("Username already exists. Please choose another.", "danger")
            return redirect("/register")

        hashed_password = generate_password_hash(password)

        new_user = User(
            username=username,
            password=hashed_password
        )

        db.session.add(new_user)
        db.session.commit()

        flash("User registered successfully! Please login.", "success")
        return redirect("/login")

    return render_template("register.html")

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        user = User.query.filter_by(
            username=request.form["username"]
        ).first()

        if user and check_password_hash(user.password, request.form["password"]):
            login_user(user)
            flash("Logged in successfully!", "success")
            return redirect("/")
        flash("Invalid username or password", "danger")

    return render_template("login.html")


@app.route("/logout")
@login_required
def logout():
    logout_user()
    flash("Logged out successfully!", "success")
    return redirect("/login")


# ------------------ SEED DATABASE ------------------

def seed_courses():
    if Course.query.first():
        return

    courses = [

# ---------------- GENERAL EDUCATION ----------------

Course(code="ENG1301", name="Composition I", 
day="Mon,Wed", time="08:00-09:15", credits=3, semester="Fall", major="GEC", prerequisite=None),

Course(code="ENG1302", name="Composition II",
day="Tue,Thu", time="08:00-09:15", credits=3, semester="Spring", major="GEC", prerequisite="ENG1301"),

Course(code="SPC1301", name="Fundamentals of Speech",
day="Mon,Wed", time="10:00-11:15", credits=3, semester="Spring", major="GEC", prerequisite=None),

Course(code="HIS2322", name="Fundamentals of Modern American History",
day="Tue,Thu", time="10:00-11:15", credits=3, semester="Fall", major="GEC", prerequisite=None),

Course(code="SCI1301", name="General Biology with Lab",
day="Mon,Wed", time="11:30-12:45", credits=4, semester="Fall", major="GEC",prerequisite=None),

Course(code="ART1301", name="Introduction to Fine Arts",
day="Tue,Thu", time="11:30-12:45", credits=3, semester="Spring", major="GEC", prerequisite=None),

Course(code="SOC1301", name="Introduction to Sociology",
day="Mon,Wed", time="13:00-14:15", credits=3, semester="Fall", major="GEC", prerequisite=None),

Course(code="EXS1220", name="Basic Concepts of Wellness",
day="Tue,Thu", time="13:00-14:15", credits=2, semester="Spring", major="GEC", prerequisite=None),

Course(code="ASE1111", name="Freshman Success Experience",
day="Fri", time="09:00-10:30", credits=2, semester="Fall", major="GEC", prerequisite=None),

# ---------------- CORE CS COURSES ----------------

Course(code="CSC1321", name="Computer Programming C++",
day="Mon,Wed", time="09:00-10:15", credits=3, semester="Fall", major="CS", prerequisite=None),

Course(code="CSC1322", name="Advanced Programming with C++",
day="Mon,Wed", time="10:30-11:45", credits=3, semester="Fall", major="CS", prerequisite="CSC1321"),

Course(code="CSC1330", name="Assembly Language Programming",
day="Tue,Thu", time="09:00-10:15", credits=3, semester="Spring", major="CS", prerequisite="CSC1322"),

Course(code="CSC2310", name="Computer Organization",
day="Tue,Thu", time="10:30-11:45", credits=3, semester="Spring", major="CS", prerequisite="CSC1322"),

Course(code="CSC2320", name="Data Structures",
day="Mon,Wed", time="12:00-13:15", credits=3, semester="Fall", major="CS", prerequisite="CSC1322"),

Course(code="CSC2340", name="Object-Oriented Programming and Design",
day="Tue,Thu", time="12:00-13:15", credits=3, semester="Fall", major="CS", prerequisite="CSC1322"),

Course(code="CSC3320", name="Programming Language Concepts",
day="Mon,Wed", time="13:30-14:45", credits=3, semester="Spring", major="CS", prerequisite="CSC2320"),

Course(code="CSC3360", name="Analysis of Algorithms",
day="Tue,Thu", time="13:30-14:45", credits=3, semester="Spring", major="CS", prerequisite="CSC2320"),

Course(code="CSC3391", name="Operating Systems",
day="Mon,Wed", time="15:00-16:15", credits=3, semester="Fall", major="CS", prerequisite="CSC2310"),

Course(code="CSC4383", name="Software Engineering",
day="Tue,Thu", time="15:00-16:15", credits=3, semester="Spring", major="CS", prerequisite="CSC2320"),

Course(code="CSC4384", name="Senior Design Project",
day="Fri", time="10:00-12:30", credits=3, semester="Spring", major="CS", prerequisite="CSC4383"),


# ---------------- CS ELECTIVES ----------------

Course(code="CSC4320", name="Artificial Intelligence",
day="Mon,Wed", time="11:00-12:15", credits=3, semester="Spring", major="CS", prerequisite="CSC3320"),

Course(code="CSC4341", name="Principles of Database Systems",
day="Tue,Thu", time="11:00-12:15", credits=3, semester="Fall", major="CS", prerequisite="CSC2320"),

Course(code="CSC4351", name="Computer Graphics",
day="Mon,Wed", time="14:00-15:15", credits=3, semester="Spring", major="CS", prerequisite="CSC2320"),

Course(code="CSC4360", name="Computer Networks",
day="Tue,Thu", time="14:00-15:15", credits=3, semester="Fall", major="CS", prerequisite="CSC2320"),

Course(code="CSC4371", name="Numerical Analysis",
day="Mon,Wed", time="08:00-09:15", credits=3, semester="Spring", major="CS", prerequisite=None),



# ---------------- MATH COURSES ----------------

Course(code="MATH1324", name="Calculus I",
day="Mon,Wed", time="09:30-10:45", credits=3, semester="Fall", major="Math", prerequisite=None),

Course(code="MATH1325", name="Calculus II",
day="Tue,Thu", time="09:30-10:45", credits=3, semester="Spring", major="Math", prerequisite="MATH1324"),

Course(code="MATH2331", name="Calculus III",
day="Tue,Thu", time="09:30-10:45", credits=3, semester="Fall", major="Math", prerequisite="MATH1325"),

Course(code="MATH3321", name="Linear Algebra",
day="Mon,Wed", time="12:30-13:45", credits=3, semester="Fall", major="Math", prerequisite="MATH1325"),

Course(code="MATH3381", name="Discrete Mathematics",
day="Tue,Thu", time="12:30-13:45", credits=3, semester="Fall", major="Math", prerequisite=None),

Course(code="STAT3311", name="Probability and Statistics",
day="Mon,Wed", time="14:30-15:45", credits=3, semester="Spring", major="Math", prerequisite="MATH1325"),
]

    db.session.add_all(courses)
    db.session.commit()


# ------------------ RUN APP ------------------

if __name__ == "__main__":
    with app.app_context():
        db.create_all()
        seed_courses()

    app.run(debug=True)