# routes/documents.py
from flask_login import login_required, current_user
from flask import Blueprint, render_template, request, flash, redirect, url_for, current_app, send_file, abort, Response

from models import DocumentCategory, Document, db, Employee, Department, Designation
from sqlalchemy import func,or_
from werkzeug.utils import secure_filename, safe_join
import os, mimetypes, datetime
from utils.notify import notify_employee, notify_many, notify_broadcast  # Add this import if notify_employee is defined in notifications.py


bp = Blueprint('documents', __name__)


@bp.app_template_filter('human_size')
def human_size(n):
    try:
        n = int(n or 0)
    except Exception:
        return "0 B"
    units = ['B','KB','MB','GB','TB','PB']
    i = 0; f = float(n)
    while f >= 1024 and i < len(units)-1:
        f /= 1024.0; i += 1
    return f"{f:.0f} {units[i]}" if i == 0 else f"{f:.1f} {units[i]}"

@bp.app_template_filter('human_date')
def human_date(dt):
    try:
        return dt.strftime('%d %b %Y') if dt else ''
    except Exception:
        return ''

# ---------- helpers ----------
def _doc_meta(doc):
    """Derive mimetype, size, and mtime from the saved file_path."""
    path = doc.file_path
    mime = mimetypes.guess_type(path or "")[0] or "application/octet-stream"
    try:
        st = os.stat(path)
        size = st.st_size
        mtime = datetime.datetime.fromtimestamp(st.st_mtime)
    except Exception:
        size = 0
        mtime = getattr(doc, "uploaded_on", None) or getattr(doc, "created_at", None)
    # Previewable types we’ll open inline
    previewable = (mime.startswith("application/pdf") or mime.startswith("image/"))
    return {"mime": mime, "size": size, "mtime": mtime, "exists": os.path.exists(path), "previewable": previewable}

# ---------- Management (unchanged) ----------
@bp.route('/document-management')
@login_required
def document_management():
    categories = DocumentCategory.query.all()
    return render_template('document/document_management.html', categories=categories)

@bp.route('/upload-document', methods=['POST'])
@login_required
def upload_document():
    category_id = request.form.get('category_id', type=int)
    file = request.files.get('file')
    if not file:
        flash("No file selected", "danger")
        return redirect(url_for('documents.document_management'))

    filename = secure_filename(file.filename)
    save_path = os.path.join(current_app.config['UPLOAD_FOLDER'], filename)
    os.makedirs(os.path.dirname(save_path), exist_ok=True)
    file.save(save_path)

    doc = Document(
        category_id=category_id,
        filename=filename,
        file_path=save_path,          # (kept as-is with your schema)
        uploaded_by=current_user.id
    )
    db.session.add(doc)
    db.session.commit()
    
    link = url_for('documents.view_documents', _external=True) + f'#cat-{DocumentCategory.id}'
    notify_broadcast(
        title="New document category",
        message=f"“{DocumentCategory.name}” has been added to Company Documents.",
        link=link,
        fanout=True,   # set False if you prefer a single NULL broadcast row
    )

    flash("Document uploaded successfully", "success")
    return redirect(url_for('documents.document_management'))

@bp.route('/add-document-category', methods=['POST'])
@login_required
def add_document_category():
    name = request.form.get('name')
    description = request.form.get('description')
    category = DocumentCategory(name=name, description=description)
    db.session.add(category)
    db.session.commit()

    link = url_for('documents.view_documents', _external=True) + f'#cat-{category.id}'
    notify_employee(
        employee_id=None,
        title="New document category",
        message=f"“{category.name}” has been added to Company Documents.",
        link=link
    )

    flash('Category added successfully', 'success')
    return redirect(url_for('documents.document_management'))

@bp.route('/delete-document-category/<int:cat_id>', methods=['POST'])
@login_required
def delete_document_category(cat_id):
    category = DocumentCategory.query.get_or_404(cat_id)
    for doc in category.documents:
        try: os.remove(doc.file_path)
        except: pass
        db.session.delete(doc)
    db.session.delete(category)
    db.session.commit()

    

    flash('Category and its documents deleted', 'success')
    return redirect(url_for('documents.document_management'))

@bp.route('/delete-document/<int:doc_id>', methods=['POST'])
@login_required
def delete_document(doc_id):
    doc = Document.query.get_or_404(doc_id)
    try: os.remove(doc.file_path)
    except: pass
    db.session.delete(doc)
    db.session.commit()
    flash('Document deleted', 'success')
    return redirect(url_for('documents.document_management'))

# ---------- New: user-friendly view with preview ----------
@bp.route('/view-documents')
@login_required
def view_documents():
    # Load categories & their docs
    categories = DocumentCategory.query.order_by(DocumentCategory.name.asc()).all()

    # Attach derived metadata (mime, size, mtime, previewable) per doc
    for c in categories:
        for d in c.documents:
            meta = _doc_meta(d)
            d._mime = meta["mime"]
            d._size = meta["size"]
            d._mtime = meta["mtime"]
            d._exists = meta["exists"]
            d._previewable = meta["previewable"]

    return render_template('document/view_documents.html', categories=categories)

@bp.route('/preview-document/<int:doc_id>')
@login_required
def preview_document(doc_id):
    doc = Document.query.get_or_404(doc_id)
    meta = _doc_meta(doc)
    if not meta["exists"]:
        abort(404)

    resp = send_file(doc.file_path, mimetype=meta["mime"], as_attachment=False, conditional=True)
    # allow embedding on same site
    resp.headers["X-Frame-Options"] = "SAMEORIGIN"
    resp.headers["Content-Security-Policy"] = "frame-ancestors 'self'"
    resp.headers["X-Content-Type-Options"] = "nosniff"
    return resp

@bp.route('/download-document/<int:doc_id>')
@login_required
def download_document(doc_id):
    doc = Document.query.get_or_404(doc_id)
    meta = _doc_meta(doc)
    if not meta["exists"]:
        abort(404)
    # Suggest the original filename
    return send_file(doc.file_path, mimetype=meta["mime"], as_attachment=True, download_name=doc.filename)




DOC_FIELDS = [
    # (column_attr_on_Employee, human label)
    ("resume_path",            "Resume"),
    ("id_proof_path",          "ID Proof"),
    ("doc_qualification",      "Qualification Docs"),
    ("doc_employment_proof",   "Employment Docs"),
    ("doc_photographs",        "Photographs"),
    ("doc_pan_card",           "PAN Card"),
    ("doc_aadhar_card",        "Aadhar Card"),
    ("doc_parents_aadhar",     "Parents Aadhar"),
    ("doc_uan_card",           "UAN Card"),
    ("doc_cancelled_cheque",   "Cancelled Cheque"),
]

@bp.route("/employee-repository")
@login_required
def employee_repository():
    """
    Employee-wise document repository with filters + pagination.
    """
    # --- filters ---
    q         = (request.args.get("q") or "").strip()
    dept_id   = request.args.get("department_id", type=int)
    desig_id  = request.args.get("designation_id", type=int)
    status    = (request.args.get("status") or "").strip()  # 'active', 'resigned', '', etc.
    page      = request.args.get("page", 1, type=int)
    per_page  = request.args.get("per_page", 12, type=int)

    query = Employee.query

    if q:
        like = f"%{q}%"
        query = query.filter(
            or_(
                Employee.full_name.ilike(like),
                Employee.employee_code.ilike(like),
                Employee.email.ilike(like),
            )
        )
    if dept_id:
        query = query.filter(Employee.department_id == dept_id)
    if desig_id:
        query = query.filter(Employee.designation_id == desig_id)
    if status:
        query = query.filter(Employee.status.ilike(status))

    query = query.order_by(Employee.full_name.asc())

    pagination = query.paginate(page=page, per_page=per_page, error_out=False)
    employees  = pagination.items

    # keep dropdown data handy
    departments  = Department.query.order_by(Department.name).all()
    designations = Designation.query.order_by(Designation.title).all()

    # Build a compact page number list (since Jinja has no min/max)
    total_pages  = pagination.pages or 1
    start        = 1 if pagination.page - 2 < 1 else pagination.page - 2
    end          = total_pages if pagination.page + 2 > total_pages else pagination.page + 2
    page_numbers = list(range(start, end + 1))

    return render_template(
        "document/employee_repository.html",
        employees=employees,
        pagination=pagination,
        page_numbers=page_numbers,
        departments=departments,
        designations=designations,
        doc_fields=DOC_FIELDS,
        # echo filters so the form stays sticky
        q=q, dept_id=dept_id, desig_id=desig_id, status=status, per_page=per_page
    )


@bp.route("/employee-doc/<int:emp_id>/<key>")
@login_required
def employee_doc_download(emp_id, key):
    """
    Serve a single employee document safely.
    `key` must be one of the names in DOC_FIELDS (e.g., 'resume_path').
    """
    # validate key
    valid_keys = {k for k, _ in DOC_FIELDS}
    if key not in valid_keys:
        abort(404)

    emp = Employee.query.get_or_404(emp_id)
    path = getattr(emp, key, None)
    if not path:
        abort(404)

    # If your app stores absolute filesystem paths in the DB, serve them directly.
    # If you store relative paths under "static/", we redirect to static.
    # Try static-style first:
    if path.startswith("static/"):
        # turn into absolute filesystem path
        fs_path = safe_join(current_app.root_path, path)
        if not fs_path or not os.path.isfile(fs_path):
            abort(404)
        return send_file(fs_path, as_attachment=True)

    # Otherwise, assume absolute filesystem path:
    if os.path.isfile(path):
        return send_file(path, as_attachment=True)

    abort(404)
