# docs.py
from flask import (
    Blueprint, render_template, request, redirect,
    url_for, flash, current_app, send_from_directory, abort
)
from flask_login import login_required, current_user
from werkzeug.utils import secure_filename
from sqlalchemy import or_, func
from datetime import datetime
import os
from typing import Optional

from extensions import db
from models import Employee, DocumentType, EmployeeDocument

docs_bp = Blueprint('docs', __name__, url_prefix='/docs')

# ---------------------------
# Helpers
# ---------------------------
def _docs_folder() -> str:
    """
    Resolve the documents folder from config or default to
    <app_root>/uploads/employee_docs, and ensure it exists.
    """
    folder = (
        current_app.config.get('DOCUMENTS_UPLOAD_FOLDER')
        or os.path.join(current_app.root_path, 'uploads', 'employee_docs')
    )
    os.makedirs(folder, exist_ok=True)
    return folder

def _is_hr(user) -> bool:
    try:
        role_name = (user.role.name if getattr(user, "role", None) else "") or ""
        return role_name.lower() in ("hr", "hr admin", "admin")
    except Exception:
        return False

def _allowed(filename: str, mimetype: Optional[str]) -> bool:
    allowed_ext = set(
        current_app.config.get('ALLOWED_DOC_EXTENSIONS')
        or {'pdf', 'png', 'jpg', 'jpeg', 'webp', 'doc', 'docx'}
    )
    ext = ''
    if filename and '.' in filename:
        ext = filename.rsplit('.', 1)[1].lower()
    if ext in allowed_ext:
        return True

    mt = (mimetype or '').lower()
    if mt in (
        'application/pdf',
        'application/msword',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    ) or mt.startswith('image/'):
        return True
    return False

def _parse_date(s: Optional[str]):
    if not s:
        return None
    try:
        return datetime.strptime(s, '%Y-%m-%d').date()
    except Exception:
        return None

# ---------------------------
# Upload document (HR only)
# ---------------------------
@docs_bp.route('/upload', methods=['GET', 'POST'])
@login_required
def upload_document():
    if not _is_hr(current_user):
        flash('❌ Only HR/Admin can upload employee documents.', 'danger')
        return redirect(url_for('leave.view_holidays'))  # or another safe page

    if request.method == 'POST':
        emp_id      = request.form.get('employee_id', type=int)
        doc_type_id = request.form.get('doc_type_id', type=int)
        title       = (request.form.get('title') or '').strip()
        issued_on   = _parse_date(request.form.get('issued_on'))
        valid_until = _parse_date(request.form.get('valid_until'))
        notes       = (request.form.get('notes') or '').strip()
        is_private  = bool(request.form.get('is_private'))

        if not emp_id or not doc_type_id or not title:
            flash('Please select employee, document type, and provide a title.', 'danger')
            return redirect(url_for('docs.upload_document'))

        f = request.files.get('file')
        if not f or not f.filename:
            flash('No file selected.', 'danger')
            return redirect(url_for('docs.upload_document'))

        if not _allowed(f.filename, f.mimetype):
            flash(f'Unsupported file type. ({f.filename or "no name"})', 'danger')
            return redirect(url_for('docs.upload_document'))

        # Ensure dependent records exist
        emp = Employee.query.get_or_404(emp_id)
        dtype = DocumentType.query.get_or_404(doc_type_id)

        # Save file
        folder = _docs_folder()
        safe_name = secure_filename(f.filename)
        ts = datetime.utcnow().strftime('%Y%m%d%H%M%S')
        stored_name = f"{emp.employee_code}_{dtype.code}_{ts}_{safe_name}"
        full_path = os.path.join(folder, stored_name)
        f.save(full_path)

        # Store only filename (relative to folder)
        rel_path = os.path.basename(full_path)

        rec = EmployeeDocument(
            employee_id=emp.id,
            doc_type_id=dtype.id,
            title=title,
            file_path=rel_path,
            original_name=safe_name,
            mime_type=f.mimetype,
            file_size=os.path.getsize(full_path),
            issued_on=issued_on,
            valid_until=valid_until,
            notes=notes,
            is_private=is_private,
            created_by_id=getattr(current_user, 'id', None)
        )
        db.session.add(rec)
        db.session.commit()
        flash('✅ Document uploaded.', 'success')
        return redirect(url_for('docs.upload_document'))

    # ⬇️ IMPORTANT: show ALL employees (not only active)
    employees = Employee.query.order_by(Employee.full_name).all()
    doc_types = DocumentType.query.order_by(DocumentType.name).all()
    return render_template('docs/upload.html', employees=employees, doc_types=doc_types)

# ---------------------------
# HR: view documents for a specific employee
# ---------------------------
@docs_bp.get('/employee/<int:emp_id>')
@login_required
def employee_docs(emp_id: int):
    if not (_is_hr(current_user) or current_user.id == emp_id):
        abort(403)

    emp = Employee.query.get_or_404(emp_id)
    rows = (
        db.session.query(EmployeeDocument, DocumentType)
        .join(DocumentType, EmployeeDocument.doc_type_id == DocumentType.id)
        .filter(EmployeeDocument.employee_id == emp.id, EmployeeDocument.is_active.is_(True))
        .order_by(
            EmployeeDocument.issued_on.is_(None),  # NULLS LAST pattern
            EmployeeDocument.issued_on.desc(),
            EmployeeDocument.created_at.desc()
        )
        .all()
    )
    return render_template('docs/employee_docs.html', emp=emp, rows=rows)

# ---------------------------
# My documents (current user)
# ---------------------------
@docs_bp.get('/my')
@login_required
def my_documents():
    emp = Employee.query.get_or_404(current_user.id)
    rows = (
        db.session.query(EmployeeDocument, DocumentType)
        .join(DocumentType, EmployeeDocument.doc_type_id == DocumentType.id)
        .filter(EmployeeDocument.employee_id == emp.id, EmployeeDocument.is_active.is_(True))
        .order_by(
            EmployeeDocument.issued_on.is_(None),
            EmployeeDocument.issued_on.desc(),
            EmployeeDocument.created_at.desc()
        )
        .all()
    )
    return render_template('docs/my_docs.html', rows=rows)

# ---------------------------
# Download (permission guard)
# ---------------------------
@docs_bp.get('/download/<int:doc_id>')
@login_required
def download_doc(doc_id: int):
    rec = EmployeeDocument.query.get_or_404(doc_id)
    if not (_is_hr(current_user) or current_user.id == rec.employee_id):
        abort(403)

    folder = _docs_folder()
    # rec.file_path is just the filename
    return send_from_directory(
        folder,
        rec.file_path,
        as_attachment=True,
        download_name=rec.original_name or os.path.basename(rec.file_path)
    )

# ---------------------------
# Soft delete (HR only)
# ---------------------------
@docs_bp.post('/delete/<int:doc_id>')
@login_required
def delete_doc(doc_id: int):
    if not _is_hr(current_user):
        abort(403)
    rec = EmployeeDocument.query.get_or_404(doc_id)
    rec.is_active = False
    db.session.commit()
    flash('🗑️ Document removed from visibility (soft delete).', 'success')
    return redirect(request.referrer or url_for('docs.upload_document'))

# ---------------------------
# List / search (HR only)
# ---------------------------
@docs_bp.get('/')
@login_required
def list_documents():
    if not _is_hr(current_user):
        abort(403)

    emp_id   = request.args.get('employee_id', type=int)
    type_id  = request.args.get('doc_type_id', type=int)
    q        = (request.args.get('q') or '').strip()
    d_from   = _parse_date(request.args.get('issued_from'))
    d_to     = _parse_date(request.args.get('issued_to'))
    page     = request.args.get('page', type=int, default=1)
    per_page = request.args.get('per_page', type=int, default=25)

    qry = (
        db.session.query(EmployeeDocument, Employee, DocumentType)
        .join(Employee, Employee.id == EmployeeDocument.employee_id)
        .join(DocumentType, DocumentType.id == EmployeeDocument.doc_type_id)
        .filter(EmployeeDocument.is_active.is_(True))
    )

    if emp_id:
        qry = qry.filter(EmployeeDocument.employee_id == emp_id)
    if type_id:
        qry = qry.filter(EmployeeDocument.doc_type_id == type_id)
    if q:
        like = f"%{q.lower()}%"
        qry = qry.filter(
            or_(
                func.lower(EmployeeDocument.title).like(like),
                func.lower(Employee.full_name).like(like),
                func.lower(Employee.employee_code).like(like),
            )
        )
    if d_from:
        qry = qry.filter(EmployeeDocument.issued_on >= d_from)
    if d_to:
        qry = qry.filter(EmployeeDocument.issued_on <= d_to)

    qry = qry.order_by(
        EmployeeDocument.issued_on.is_(None),
        EmployeeDocument.issued_on.desc(),
        EmployeeDocument.created_at.desc()
    )

    pagination = qry.paginate(page=page, per_page=per_page, error_out=False)
    rows = pagination.items

    # ⬇️ For filters, show all employees + all doc types
    employees = Employee.query.order_by(Employee.full_name).all()
    doc_types = DocumentType.query.order_by(DocumentType.name).all()

    return render_template(
        'docs/index.html',
        rows=rows, employees=employees, doc_types=doc_types,
        emp_id=emp_id, type_id=type_id, q=q,
        issued_from=(d_from.isoformat() if d_from else ''),
        issued_to=(d_to.isoformat() if d_to else ''),
        pagination=pagination
    )

# ---------------------------
# AJAX: employee search (HR only)
# ---------------------------
@docs_bp.get('/employee/search')
@login_required
def employee_search():
    if not _is_hr(current_user):
        abort(403)

    q_text = (request.args.get('q') or '').strip().lower()
    page = request.args.get('page', type=int, default=1)
    per  = request.args.get('per_page', type=int, default=20)

    qry = Employee.query  # ⬅️ include ALL employees regardless of status
    if q_text:
        like = f"%{q_text}%"
        qry = qry.filter(
            or_(
                func.lower(Employee.full_name).like(like),
                func.lower(Employee.employee_code).like(like),
                func.lower(Employee.email).like(like),
            )
        )

    items = (
        qry.order_by(Employee.full_name)
        .offset((page - 1) * per)
        .limit(per)
        .all()
    )

    # Include status in the label for clarity
    results = [
        {
            "id": e.id,
            "text": f"{e.employee_code} — {e.full_name}"
                    f"{' (' + e.status + ')' if e.status else ''}"
        }
        for e in items
    ]
    return {"results": results}