from flask import Blueprint, render_template, request, redirect, url_for, flash, current_app
from flask_login import login_required, current_user
from datetime import date, datetime
from werkzeug.utils import secure_filename
import os

from models import (
    db, BroadcastMessage, BroadcastRead,
    BroadcastDepartment, BroadcastRole,
    Department, Role, Employee,
    DeviceToken, Notification
)
from utils.notify import notify_many, notify_broadcast
from utils.push import send_push

broadcast_bp = Blueprint('broadcast', __name__)

def _annc_link(broadcast_id: int) -> str:
    return url_for('broadcast.view_announcements', _external=True) + f"#b-{broadcast_id}"

def _truncate(text: str, n=160) -> str:
    text = (text or "").strip()
    return (text[: n - 1] + "…") if len(text) > n else text


# ---------------- PUSH helpers (local to broadcast.py) ----------------
def _deactivate_tokens(bad_tokens: list[str]):
    bad_tokens = [t for t in (bad_tokens or []) if t]
    if not bad_tokens:
        return
    DeviceToken.query.filter(DeviceToken.token.in_(bad_tokens)).update(
        {DeviceToken.is_active: False, DeviceToken.last_seen_at: datetime.utcnow()},
        synchronize_session=False
    )
    db.session.commit()


def _push_to_many(employee_ids: list[int], title: str, message: str, link: str, notif_type: str = "info"):
    if not employee_ids:
        return {"sent": 0, "failed": 0, "invalid": 0}

    # store in-app per employee
    for eid in employee_ids:
        db.session.add(Notification(
            employee_id=eid,
            title=title[:255],
            message=message,
            type=notif_type,
            link=link,
            is_read=False,
        ))
    db.session.commit()

    tokens = (
        DeviceToken.query
        .filter(DeviceToken.employee_id.in_(employee_ids), DeviceToken.is_active.is_(True))
        .with_entities(DeviceToken.token)
        .all()
    )
    token_list = [t[0] for t in tokens if t and t[0]]

    try:
        if not token_list:
            return {"sent": 0, "failed": 0, "invalid": 0}

        return send_push(
            token_list,
            title=title,
            body=message,
            data={"link": link, "type": notif_type},
            on_invalid_tokens=_deactivate_tokens,
        )
    except Exception:
        current_app.logger.exception("Broadcast push send failed")
        return {"sent": 0, "failed": len(token_list or []), "invalid": 0}


# ---------- Create/List ----------
from sqlalchemy import or_
from datetime import datetime, date

@broadcast_bp.route('/broadcasts', methods=['GET', 'POST'])
@login_required
def manage_broadcasts():
    role_name = (getattr(getattr(current_user, "role", None), "name", "") or "").lower()
    if role_name not in {"admin", "hr", "hr admin"}:
        flash('❌ Only Admin / HR / HR Admin can manage broadcasts.', 'danger')
        return redirect(url_for('employee.dashboard'))

    ALLOWED_EXTENSIONS = {'jpg', 'jpeg', 'png', 'webp', 'pdf', 'doc', 'docx', 'xls', 'xlsx'}
    def allowed_file(fn): return '.' in fn and fn.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

    # -------------------- FILTERS (GET) --------------------
    q = (request.args.get("q") or "").strip()
    dept_id = (request.args.get("dept_id") or "").strip()   # "" | "none" | "<id>"
    active_only = (request.args.get("active") or "").strip()  # "" | "1"

    if request.method == 'POST':
        title = (request.form.get('title') or '').strip()
        content = request.form.get('content') or ''

        # ✅ IMPORTANT: store as date objects (not strings) so comparisons work everywhere
        sd_raw = (request.form.get('start_date') or '').strip()
        ed_raw = (request.form.get('end_date') or '').strip()

        start_date = None
        end_date = None
        try:
            if sd_raw:
                start_date = datetime.strptime(sd_raw, "%Y-%m-%d").date()
        except Exception:
            flash("❌ Invalid Start Date.", "danger")
            return redirect(url_for('broadcast.manage_broadcasts'))

        try:
            if ed_raw:
                end_date = datetime.strptime(ed_raw, "%Y-%m-%d").date()
        except Exception:
            flash("❌ Invalid End Date.", "danger")
            return redirect(url_for('broadcast.manage_broadcasts'))

        # Attachment (optional)
        attachment = request.files.get('attachment')
        attachment_path = None
        if attachment and attachment.filename:
            if not allowed_file(attachment.filename):
                flash('❌ Invalid file type. Allowed: JPG, PNG, PDF, DOCX, XLSX, etc.', 'danger')
                return redirect(url_for('broadcast.manage_broadcasts'))
            filename = secure_filename(attachment.filename)
            upload_dir = os.path.join('static', 'uploads')
            os.makedirs(upload_dir, exist_ok=True)
            attachment_path = os.path.join(upload_dir, filename)
            attachment.save(attachment_path)

        # Create broadcast
        b = BroadcastMessage(
            title=title,
            content=content,
            attachment_path=attachment_path,
            start_date=start_date,
            end_date=end_date,
            created_by=current_user.id
        )
        db.session.add(b)
        db.session.flush()  # get b.id

        # Departments
        selected_department_ids = request.form.getlist('department_ids') or []
        if 'all' in selected_department_ids:
            selected_department_ids = [str(d.id) for d in Department.query.all()]

        dept_ids = []
        for v in selected_department_ids:
            try:
                dept_ids.append(int(v))
            except Exception:
                pass
        dept_ids = sorted(set(dept_ids))

        for did in dept_ids:
            db.session.add(BroadcastDepartment(broadcast_id=b.id, department_id=did))

        db.session.commit()
        flash('✅ Broadcast message added.', 'success')

        # 🔔 Notifications (unchanged)
        link = _annc_link(b.id)
        title_for_bell = f"New announcement: {title}"
        msg_for_bell   = _truncate(content, 160)

        if dept_ids:
            recipients = [
                e.id for e in Employee.query
                .filter(
                    Employee.status.in_(["active", "notice", "Active", "Notice"]),
                    Employee.department_id.in_(dept_ids)
                ).all()
            ]
            if recipients:
                notify_many(recipients, title_for_bell, msg_for_bell, link)
                _push_to_many(recipients, title_for_bell, msg_for_bell, link, notif_type="info")
        else:
            recipients = [
                e.id for e in Employee.query
                .filter(Employee.status.in_(["active", "notice", "Active", "Notice"]))
                .all()
            ]
            notify_broadcast(title_for_bell, msg_for_bell, link, fanout=True)
            _push_to_many(recipients, title_for_bell, msg_for_bell, link, notif_type="info")

        return redirect(url_for('broadcast.manage_broadcasts'))

    # ✅ FILTERED list
    query = BroadcastMessage.query

    if q:
        like = f"%{q}%"
        query = query.filter(or_(
            BroadcastMessage.title.ilike(like),
            BroadcastMessage.content.ilike(like),
        ))

    if dept_id:
        if dept_id == "none":
            query = query.filter(~BroadcastMessage.departments.any())
        else:
            try:
                did = int(dept_id)
                # departments relationship is usually to Department; this is the safest form:
                query = query.filter(BroadcastMessage.departments.any(Department.id == did))
            except Exception:
                pass

    if active_only == "1":
        today = date.today()
        query = query.filter(
            ((BroadcastMessage.start_date == None) | (BroadcastMessage.start_date <= today)),
            ((BroadcastMessage.end_date   == None) | (BroadcastMessage.end_date   >= today)),
        )

    broadcasts = query.order_by(BroadcastMessage.created_on.desc()).all()
    departments = Department.query.order_by(Department.name).all()

    return render_template(
        'admin/manage_broadcasts.html',
        broadcasts=broadcasts,
        departments=departments,
        q=q, dept_id=dept_id, active_only=active_only
    )

@broadcast_bp.route('/broadcasts/delete/<int:broadcast_id>', methods=['POST'])
@login_required
def delete_broadcast(broadcast_id):
    role_name = (getattr(getattr(current_user, "role", None), "name", "") or "").lower()
    if role_name not in {"admin", "hr", "hr admin"}:
        flash('❌ Only Admin / HR / HR Admin can delete broadcasts.', 'danger')
        return redirect(url_for('employee.dashboard'))

    b = BroadcastMessage.query.get_or_404(broadcast_id)

    # 1) Delete link rows first (prevents FK constraint issues)
    BroadcastDepartment.query.filter_by(broadcast_id=b.id).delete(synchronize_session=False)
    BroadcastRole.query.filter_by(broadcast_id=b.id).delete(synchronize_session=False)
    BroadcastRead.query.filter_by(broadcast_id=b.id).delete(synchronize_session=False)

    # 2) Delete attachment file (optional)
    try:
        if b.attachment_path and os.path.exists(b.attachment_path):
            os.remove(b.attachment_path)
    except Exception:
        current_app.logger.exception("Could not delete broadcast attachment")

    # 3) Delete parent
    db.session.delete(b)
    db.session.commit()

    flash('✅ Broadcast message deleted.', 'success')
    return redirect(url_for('broadcast.manage_broadcasts'))


@broadcast_bp.route('/broadcasts/mark-read/<int:broadcast_id>', methods=['POST'])
@login_required
def mark_read(broadcast_id):
    already = BroadcastRead.query.filter_by(
        broadcast_id=broadcast_id, employee_id=current_user.id
    ).first()
    if not already:
        db.session.add(BroadcastRead(broadcast_id=broadcast_id, employee_id=current_user.id))
        db.session.commit()
        flash('✅ Marked as read.', 'success')
    else:
        flash('ℹ️ Already marked as read.', 'info')
    return redirect(url_for('employee.dashboard'))

# ---------- View Announcements (for employees) ----------

@broadcast_bp.route('/announcements')
@login_required
def view_announcements():
    today = date.today()
    dept_id = current_user.department_id
    role_id = current_user.role_id

    broadcasts = (
        BroadcastMessage.query
        .filter(
            or_(BroadcastMessage.start_date == None, BroadcastMessage.start_date <= today),
            or_(BroadcastMessage.end_date == None, BroadcastMessage.end_date >= today),

            or_(~BroadcastMessage.departments.any(),
                BroadcastMessage.departments.any(Department.id == dept_id)),

            or_(~BroadcastMessage.roles.any(),
                BroadcastMessage.roles.any(Role.id == role_id)),
        )
        .order_by(BroadcastMessage.created_on.desc())
        .all()
    )

    return render_template('broadcast/announcements.html', broadcasts=broadcasts)