from flask import Blueprint, render_template, request, jsonify, abort
from flask_login import login_required, current_user
from sqlalchemy import asc
from extensions import db
from models import Dashboard, DashboardWidget
from models import Role  # if needed to check role name

dashboard_bp = Blueprint('dashbuild', __name__, url_prefix='/dashboard')

def _is_hr_or_admin():
    try:
        r = (getattr(getattr(current_user, 'role', None), 'name', '') or '').lower()
        return r in ('admin','hr','hr admin')
    except:
        return False

# ----- Widget Catalog (register new widgets here) -----
def get_widget_catalog():
    """
    id: unique widget_type
    name: human label
    kind: 'chart'|'kpi'|'table'
    """
    return [
        {"id":"attendance_by_day","name":"Attendance — This Month (Line)","kind":"chart"},
        {"id":"pending_leaves","name":"Pending Leave Requests (KPI)","kind":"kpi"},
        {"id":"dept_headcount","name":"Headcount by Department (Bar)","kind":"chart"},
        {"id":"payroll_status","name":"Payroll Status — Selected Month (KPI)","kind":"kpi"},
        {"id":"birthdays_month",   "name":"Birthdays This Month (List)",        "kind":"table"},
        {"id":"leaves_by_status",  "name":"Leave Requests by Status (Donut)",   "kind":"chart"},
    ]

# ----- Select or create dashboard -----
def _get_or_create_dashboard():
    # Per-user dashboard for HR/Admin, else abort
    if not _is_hr_or_admin():
        abort(403)

    dash = (Dashboard.query
            .filter_by(owner_user_id=current_user.id)
            .order_by(Dashboard.id.asc())
            .first())
    if not dash:
        dash = Dashboard(owner_user_id=current_user.id, title='My Dashboard')
        db.session.add(dash)
        db.session.commit()
    return dash

# ----- Builder UI -----
@dashboard_bp.route('/builder', methods=['GET'])
@login_required
def builder():
    if not _is_hr_or_admin(): abort(403)
    dash = _get_or_create_dashboard()
    widgets = (DashboardWidget.query
               .filter_by(dashboard_id=dash.id)
               .order_by(asc(DashboardWidget.order_index))
               .all())
    return render_template('dashboard/builder.html',
                           dashboard=dash,
                           widgets=widgets,
                           widget_catalog=get_widget_catalog())

# ----- Render current dashboard (view mode) -----
@dashboard_bp.route('/my', methods=['GET'])
@login_required
def my_dashboard():
    if not _is_hr_or_admin(): abort(403)
    dash = _get_or_create_dashboard()
    widgets = (DashboardWidget.query
               .filter_by(dashboard_id=dash.id)
               .order_by(asc(DashboardWidget.order_index))
               .all())
    return render_template('dashboard/view.html', dashboard=dash, widgets=widgets)

# ----- API: Add widget -----
@dashboard_bp.post('/api/add-widget')
@login_required
def api_add_widget():
    if not _is_hr_or_admin(): abort(403)
    dash = _get_or_create_dashboard()
    data = request.get_json() or {}
    wtype = data.get('widget_type')
    if not any(w['id']==wtype for w in get_widget_catalog()):
        return jsonify({"ok":False,"error":"Unknown widget"}), 400
    # append at end
    max_order = db.session.query(db.func.max(DashboardWidget.order_index)).filter_by(dashboard_id=dash.id).scalar() or 0
    w = DashboardWidget(
        dashboard_id=dash.id,
        widget_type=wtype,
        title=data.get('title') or None,
        config_json=data.get('config') or {},
        x=0, y=0, w=6, h=3,
        order_index=max_order + 1
    )
    db.session.add(w); db.session.commit()
    return jsonify({"ok":True, "widget_id": w.id})

# ----- API: Save layout (positions/sizes + titles/configs) -----
@dashboard_bp.post('/api/save-layout')
@login_required
def api_save_layout():
    if not _is_hr_or_admin(): abort(403)
    dash = _get_or_create_dashboard()
    items = request.get_json() or []
    # items: [{id,x,y,w,h,order_index,title?,config?}, ...]
    ids = {i.get('id') for i in items if i.get('id')}
    db_widgets = {w.id: w for w in DashboardWidget.query.filter(DashboardWidget.dashboard_id==dash.id,
                                                                 DashboardWidget.id.in_(ids)).all()}
    for i in items:
        wid = int(i['id'])
        w = db_widgets.get(wid)
        if not w: continue
        w.x = int(i.get('x', w.x)); w.y = int(i.get('y', w.y))
        w.w = int(i.get('w', w.w)); w.h = int(i.get('h', w.h))
        w.order_index = int(i.get('order_index', w.order_index))
        if 'title' in i: w.title = i['title']
        if 'config' in i: w.config_json = i['config']
    db.session.commit()
    return jsonify({"ok":True})

# ----- API: Remove widget -----
@dashboard_bp.post('/api/remove-widget')
@login_required
def api_remove_widget():
    if not _is_hr_or_admin(): abort(403)
    wid = (request.get_json() or {}).get('id')
    w = DashboardWidget.query.get_or_404(wid)
    # ensure ownership
    dash = _get_or_create_dashboard()
    if w.dashboard_id != dash.id: abort(403)
    db.session.delete(w); db.session.commit()
    return jsonify({"ok":True})

# ----- API: Render a widget’s HTML (used by builder/view) -----
@dashboard_bp.get('/api/render-widget/<int:widget_id>')
@login_required
def api_render_widget(widget_id):
    w = DashboardWidget.query.get_or_404(widget_id)
    dash = _get_or_create_dashboard()
    if w.dashboard_id != dash.id: abort(403)
    # render partial based on widget type
    html = _render_widget_html(w)
    return jsonify({"ok":True, "html": html})

# ----- Widget renderer (server-side) -----
from flask import render_template_string
def _render_widget_html(w: DashboardWidget) -> str:
    # lightweight: choose a partial by type
    ctx = dict(w=w, cfg=w.config_json or {})
    if w.widget_type == 'pending_leaves':
        # fake number or query your LeaveRequest model (status='pending')
        from models import LeaveRequest
        pending_count = LeaveRequest.query.filter_by(status='pending').count()
        ctx['value'] = pending_count
        tpl = """
        <div class="card shadow-sm h-100">
          <div class="card-body">
            <div class="d-flex justify-content-between align-items-center">
              <div>
                <div class="text-muted small">Pending Leaves</div>
                <div class="display-6 fw-bold">{{ value }}</div>
              </div>
              <i class="bi bi-calendar2-x fs-1 text-secondary"></i>
            </div>
          </div>
        </div>"""
        return render_template_string(tpl, **ctx)

    if w.widget_type == 'dept_headcount':
        # Example: produce labels+values (replace with your real query)
        from models import Department, Employee
        rows = db.session.query(Department.name, db.func.count(Employee.id))\
              .join(Employee, Employee.department_id==Department.id)\
              .group_by(Department.name).all()
        labels = [r[0] for r in rows]
        data = [int(r[1]) for r in rows]
        ctx.update(labels=labels, data=data)
        tpl = """
        <div class="card shadow-sm h-100">
          <div class="card-body">
            <div class="d-flex justify-content-between mb-2">
              <strong>{{ w.title or 'Headcount by Department' }}</strong>
            </div>
            <canvas id="chart-{{ w.id }}"></canvas>
          </div>
        </div>
        <script>
          (function(){
            const ctx = document.getElementById('chart-{{ w.id }}').getContext('2d');
            new Chart(ctx, {
              type: 'bar',
              data: { labels: {{ labels|tojson }}, datasets:[{ label: 'Employees', data: {{ data|tojson }} }] },
              options: { responsive:true, maintainAspectRatio:false }
            });
          })();
        </script>"""
        return render_template_string(tpl, **ctx)

    if w.widget_type == 'attendance_by_day':
        # Example: dummy series; replace with real attendance series for current month
        labels = [str(d) for d in range(1, 31)]
        data = [max(0, 50 + ((i*7)%20) - ((i*3)%10)) for i in range(1, 31)]
        ctx.update(labels=labels, data=data)
        tpl = """
        <div class="card shadow-sm h-100">
          <div class="card-body">
            <div class="d-flex justify-content-between mb-2">
              <strong>{{ w.title or 'Attendance — This Month' }}</strong>
            </div>
            <canvas id="chart-{{ w.id }}"></canvas>
          </div>
        </div>
        <script>
          (function(){
            const ctx = document.getElementById('chart-{{ w.id }}').getContext('2d');
            new Chart(ctx, {
              type: 'line',
              data: { labels: {{ labels|tojson }}, datasets:[{ label: 'Present', data: {{ data|tojson }} }] },
              options: { responsive:true, maintainAspectRatio:false }
            });
          })();
        </script>"""
        return render_template_string(tpl, **ctx)

    if w.widget_type == 'payroll_status':
        # Example KPI (replace with your payroll freeze status)
        ctx['label'] = 'Payroll Ready'
        ctx['value'] = 'Yes'  # or 'No'
        tpl = """
        <div class="card shadow-sm h-100">
          <div class="card-body">
            <div class="text-muted small">{{ label }}</div>
            <div class="display-6 fw-bold">{{ value }}</div>
          </div>
        </div>"""
        return render_template_string(tpl, **ctx)
    
    if w.widget_type == 'birthdays_month':
        from datetime import date
        from models import Employee
        today = date.today()
        rows = (Employee.query
                .filter(db.extract('month', Employee.dob) == today.month)
                .order_by(db.extract('day', Employee.dob).asc())
                .with_entities(Employee.full_name, db.extract('day', Employee.dob).label('d'))
                .all())
        ctx['rows'] = rows
        tpl = """
        <div class="card shadow-sm h-100">
          <div class="card-body">
            <strong>{{ w.title or 'Birthdays This Month' }}</strong>
            <ul class="list-unstyled mt-3 mb-0 small">
              {% if rows %}
                {% for name, d in rows %}
                  <li class="py-1 d-flex justify-content-between">
                    <span>{{ name }}</span>
                    <span class="text-muted"> {{ '%02d'|format(d) }}</span>
                  </li>
                {% endfor %}
              {% else %}
                <li class="text-muted">No birthdays this month.</li>
              {% endif %}
            </ul>
          </div>
        </div>"""
        return render_template_string(tpl, **ctx)

    # Leave requests by status (donut)
    if w.widget_type == 'leaves_by_status':
        from models import LeaveRequest
        rows = (db.session.query(LeaveRequest.status, db.func.count(LeaveRequest.id))
                .group_by(LeaveRequest.status).all())
        labels = [r[0] or 'Unknown' for r in rows]
        data   = [int(r[1]) for r in rows]
        ctx.update(labels=labels, data=data)
        tpl = """
        <div class="card shadow-sm h-100">
          <div class="card-body">
            <div class="d-flex justify-content-between mb-2">
              <strong>{{ w.title or 'Leave Requests by Status' }}</strong>
            </div>
            <div style="height: 240px;"><canvas id="chart-{{ w.id }}"></canvas></div>
          </div>
        </div>
        <script>
          (function(){
            const ctx = document.getElementById('chart-{{ w.id }}').getContext('2d');
            new Chart(ctx, {
              type: 'doughnut',
              data: { labels: {{ labels|tojson }}, datasets:[{ data: {{ data|tojson }} }] },
              options: { responsive:true, maintainAspectRatio:false, plugins:{ legend:{ position:'bottom' } } }
            });
          })();
        </script>"""
        return render_template_string(tpl, **ctx)

    return render_template_string("<div class='card'><div class='card-body'>Unknown widget.</div></div>")

    # Fallback
    return render_template_string("<div class='card'><div class='card-body'>Unknown widget.</div></div>")

