from datetime import datetime, timedelta

from flask import Blueprint, abort, flash, redirect, render_template, request, url_for
from flask_login import current_user
from sqlalchemy import func

from app.auth.access import roles_required
from app.extensions import db
from app.models import (
    Activity,
    ActivityRegistration,
    Donation,
    HeartTransaction,
    ManagerApprovalHistory,
    ManagerApprovalRequest,
    Notification,
)
from app.models.enums import ActivityStatus, ApprovalStatus, DonationStatus, RegistrationStatus
from app.notifications.service import NotificationService

bp = Blueprint("employee", __name__, url_prefix="/employee")

EMPLOYEE_PORTAL_ROLES = (
    "EMPLOYEE", "MANAGER", "SUPER_ADMIN", "PIRAMAL_ADMIN", "INDIAISUS_ADMIN",
)


@bp.route("/dashboard")
@roles_required(*EMPLOYEE_PORTAL_ROLES)
def dashboard():
    employee = current_user.employee
    upcoming = []
    volunteer_hours = 0
    completed_count = 0
    donation_total = 0
    hearts = 0
    recommended = Activity.query.filter_by(status=ActivityStatus.PUBLISHED).order_by(Activity.start_datetime.asc()).limit(6).all()
    if employee:
        upcoming = ActivityRegistration.query.join(Activity).filter(
            ActivityRegistration.employee_id == employee.id,
            ActivityRegistration.status.in_([RegistrationStatus.PENDING_APPROVAL, RegistrationStatus.CONFIRMED]),
        ).order_by(Activity.start_datetime.asc()).limit(5).all()
        volunteer_hours = float(ActivityRegistration.query.with_entities(func.coalesce(func.sum(ActivityRegistration.volunteer_hours), 0)).filter_by(employee_id=employee.id).scalar() or 0)
        completed_count = ActivityRegistration.query.filter_by(employee_id=employee.id, status=RegistrationStatus.COMPLETED).count()
        donation_total = float(Donation.query.with_entities(func.coalesce(func.sum(Donation.amount), 0)).filter_by(employee_id=employee.id, status=DonationStatus.SUCCESSFUL).scalar() or 0)
        hearts = int(HeartTransaction.query.with_entities(func.coalesce(func.sum(HeartTransaction.hearts), 0)).filter_by(employee_id=employee.id).scalar() or 0)
    return render_template(
        "employee/dashboard.html",
        employee=employee,
        upcoming=upcoming,
        volunteer_hours=volunteer_hours,
        completed_count=completed_count,
        donation_total=donation_total,
        hearts=hearts,
        recommended=recommended,
    )


@bp.route("/my-seva")
@roles_required(*EMPLOYEE_PORTAL_ROLES)
def my_seva():
    employee = current_user.employee
    registrations = []
    approvals = {}
    if employee:
        registrations = ActivityRegistration.query.filter_by(employee_id=employee.id).order_by(ActivityRegistration.created_at.desc()).all()
        registration_ids = [r.id for r in registrations]
        if registration_ids:
            approvals = {a.registration_id: a for a in ManagerApprovalRequest.query.filter(ManagerApprovalRequest.registration_id.in_(registration_ids)).all()}
    return render_template("employee/my_seva.html", registrations=registrations, approvals=approvals)


@bp.route("/my-seva/<int:registration_id>/cancel", methods=["POST"])
@roles_required(*EMPLOYEE_PORTAL_ROLES)
def cancel_registration(registration_id):
    employee = current_user.employee
    registration = db.session.get(ActivityRegistration, registration_id)
    if not employee or not registration or registration.employee_id != employee.id:
        abort(404)
    if registration.status in [RegistrationStatus.COMPLETED, RegistrationStatus.NO_SHOW, RegistrationStatus.CANCELLED]:
        flash("This registration can no longer be cancelled.", "warning")
        return redirect(url_for("employee.my_seva"))

    cutoff_hours = registration.activity.cancellation_cutoff_hours or 0
    if registration.activity.start_datetime and datetime.utcnow() > registration.activity.start_datetime - timedelta(hours=cutoff_hours):
        flash("The cancellation cutoff has passed. Please contact the Seva Setu team.", "danger")
        return redirect(url_for("employee.my_seva"))

    reason = request.form.get("reason", "").strip()
    registration.status = RegistrationStatus.CANCELLED
    registration.cancelled_at = datetime.utcnow()
    registration.cancellation_reason = reason
    registration.approval_status = ApprovalStatus.WITHDRAWN if registration.approval_status == ApprovalStatus.PENDING else registration.approval_status

    approval = ManagerApprovalRequest.query.filter_by(registration_id=registration.id).first()
    if approval and approval.status == ApprovalStatus.PENDING:
        old_status = approval.status
        approval.status = ApprovalStatus.WITHDRAWN
        approval.decided_at = datetime.utcnow()
        db.session.add(ManagerApprovalHistory(
            approval_request_id=approval.id,
            action="WITHDRAW",
            from_status=old_status,
            to_status=ApprovalStatus.WITHDRAWN,
            acted_by_user_id=current_user.id,
            remarks=reason or "Withdrawn by employee",
        ))

    NotificationService.create_in_app(
        employee.id,
        "REGISTRATION_CANCELLED",
        "Registration cancelled",
        f"Your registration for {registration.activity.title} has been cancelled.",
        url_for("employee.my_seva"),
    )
    db.session.commit()
    flash("Your registration has been cancelled.", "success")
    return redirect(url_for("employee.my_seva"))


@bp.route("/approvals/<int:request_id>/nudge", methods=["POST"])
@roles_required(*EMPLOYEE_PORTAL_ROLES)
def nudge_manager(request_id):
    employee = current_user.employee
    approval = db.session.get(ManagerApprovalRequest, request_id)
    if not employee or not approval or approval.employee_id != employee.id:
        abort(404)
    if approval.status != ApprovalStatus.PENDING:
        flash("This approval request is no longer pending.", "warning")
        return redirect(url_for("employee.my_seva"))

    now = datetime.utcnow()
    cooling_period = timedelta(hours=24)
    if approval.last_nudged_at and now - approval.last_nudged_at < cooling_period:
        next_time = approval.last_nudged_at + cooling_period
        flash(f"You can nudge your manager again after {next_time.strftime('%d %b %Y %I:%M %p')}.", "warning")
        return redirect(url_for("employee.my_seva"))

    approval.last_nudged_at = now
    approval.nudge_count = (approval.nudge_count or 0) + 1
    NotificationService.create_in_app(
        approval.manager_id,
        "MANAGER_APPROVAL_NUDGE",
        "Approval reminder",
        f"{employee.full_name} has reminded you about the volunteering request for {approval.registration.activity.title}.",
        url_for("manager.approval_detail", request_id=approval.id),
    )
    db.session.commit()
    flash("Your manager has been nudged.", "success")
    return redirect(url_for("employee.my_seva"))


@bp.route("/notifications")
@roles_required(*EMPLOYEE_PORTAL_ROLES)
def notifications():
    employee = current_user.employee
    rows = []
    if employee:
        rows = Notification.query.filter_by(employee_id=employee.id).order_by(Notification.created_at.desc()).limit(100).all()
    return render_template("employee/notifications.html", notifications=rows)


@bp.route("/notifications/<int:notification_id>/read", methods=["POST"])
@roles_required(*EMPLOYEE_PORTAL_ROLES)
def notification_read(notification_id):
    employee = current_user.employee
    notification = db.session.get(Notification, notification_id)
    if not employee or not notification or notification.employee_id != employee.id:
        abort(404)
    notification.read_at = notification.read_at or datetime.utcnow()
    db.session.commit()
    if notification.link_url:
        return redirect(notification.link_url)
    return redirect(url_for("employee.notifications"))
