# attendance.py

import calendar
from calendar import monthrange
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify
from flask_login import login_required, current_user
from datetime import datetime, date, timedelta
from sqlalchemy import func, and_, or_

from extensions import db
from models import (
    Attendance, Holiday, WeekendSettings, Employee, DesktimeLog,
    Payroll, LeaveRequest, SystemSetting
)
from typing import Optional
from .attendance_engine import recalc_attendance_for_day

# =========================
# Helpers: attendance config for display thresholds
# =========================

_DEFAULT_CFG = {"default": {"min_hours_present": 8.0, "min_hours_half_day": 4.0}}

def _load_priority_cfg() -> dict:
    try:
        s = SystemSetting.query.filter_by(key="attendance_source_priority").first()
        if not s or not getattr(s, "json_value", None):
            return _DEFAULT_CFG
        import json
        cfg = json.loads(s.json_value)
        if isinstance(cfg, dict) and isinstance(cfg.get("default"), dict):
            return cfg
    except Exception:
        pass
    return _DEFAULT_CFG

def _display_thresholds_for_employee(emp: Employee) -> tuple[float, float]:
    cfg = _load_priority_cfg()
    block = cfg.get("default", {}) or {}
    # if you later add work-mode blocks, you can resolve them here
    min_p = float(block.get("min_hours_present", 8.0))
    min_h = float(block.get("min_hours_half_day", 4.0))
    return min_p, min_h

def _compute_effective_status_for_display(
    a: Attendance,
    *,
    min_present: float,
    min_half: float,
    weekend_dates: set,
) -> str:
    """
    Decide what to SHOW in calendar.

    Priority:
    - If remarks is Holiday/Weekend/Leave -> show that
    - Else if IN+OUT exists OR working_hours exists -> compute Present/Half/LWP by hours
    - Else fall back to remarks
    """
    r = (a.remarks or "").strip()

    if r.lower() in ("holiday", "weekend", "leave"):
        return r.title() if r.lower() != "lwp" else "LWP"

    # If remarks empty but date is weekend, show Weekend
    if not r and a.date in weekend_dates:
        return "Weekend"

    # Prefer recompute from hours if we have any usable time/hours
    hours = None

    if a.check_in_time and a.check_out_time:
        dt_in = datetime.combine(a.date, a.check_in_time)
        dt_out = datetime.combine(a.date, a.check_out_time)
        if dt_out > dt_in:
            hours = (dt_out - dt_in).total_seconds() / 3600.0

    if hours is None and isinstance(a.working_hours, (int, float)):
        hours = float(a.working_hours)

    if hours is not None and hours > 0:
        if hours >= min_present:
            return "Present"
        elif hours >= min_half:
            return "Half Day"
        else:
            return "LWP"

    # fallback to remarks as-is
    if r:
        # normalize common values
        rl = r.lower()
        if rl in ("present",):
            return "Present"
        if rl in ("half day", "half_day", "halfday"):
            return "Half Day"
        if rl in ("lwp", "absent"):
            return "LWP"
        return r

    return ""


# =========================
# Core monthly processor (reusable for UI + CLI)
# =========================

def process_attendance_for_employee_month(
    emp: Employee,
    year: int,
    month: int,
    *,
    today: Optional[date] = None,
):
    """
    Core monthly attendance processor for a single employee and month.

    - Applies weekend/holiday/RH/leave logic.
    - For normal working days, calls recalc_attendance_for_day()
    """
    import calendar as _cal

    if today is None:
        today = date.today()

    start_date = date(year, month, 1)
    month_end = date(year, month, _cal.monthrange(year, month)[1])
    end_date = min(month_end, today) if (year, month) == (today.year, today.month) else month_end

    # No overlap: employee joins after the end of this month
    if emp.date_of_joining and emp.date_of_joining > end_date:
        return {
            "processed": False,
            "reason": "joins_after_range",
            "emp_id": emp.id,
            "start": None,
            "end": None,
        }

    emp_start_date = max(start_date, emp.date_of_joining or start_date)

    weekend = WeekendSettings.query.order_by(WeekendSettings.updated_on.desc()).first()

    def get_saturday_status(settings, day):
        if not settings:
            return None
        week_number = (day.day - 1) // 7 + 1
        slot = ["first", "second", "third", "fourth", "fifth"][week_number - 1]
        return getattr(settings, f"{slot}_saturday", None)

    # --- Approved leaves (ALL types) ---
    approved_leaves_all = LeaveRequest.query.filter_by(
        employee_id=emp.id, status='final_approved'
    ).filter(
        LeaveRequest.start_date <= end_date,
        LeaveRequest.end_date >= emp_start_date
    ).all()

    # Split RH vs non-RH
    approved_rh_dates = set()
    approved_leave_dates_non_rh = set()
    for lr in approved_leaves_all:
        d0 = max(lr.start_date, emp_start_date)
        d1 = min(lr.end_date, end_date)
        cur = d0
        if _is_rh_leave(getattr(lr, "leave_type", None)):
            while cur <= d1:
                approved_rh_dates.add(cur)
                cur += timedelta(days=1)
        else:
            while cur <= d1:
                approved_leave_dates_non_rh.add(cur)
                cur += timedelta(days=1)

    # Preload existing attendance rows in range
    attendance_map = {
        att.date: att
        for att in Attendance.query.filter_by(employee_id=emp.id)
        .filter(and_(Attendance.date >= emp_start_date, Attendance.date <= end_date))
        .all()
    }

    processed_days = 0
    cur_date = emp_start_date

    while cur_date <= end_date:
        # Weekend flags
        is_weekend = False
        if weekend:
            if cur_date.weekday() == 5 and _norm(get_saturday_status(weekend, cur_date)) == 'off':
                is_weekend = True
            if cur_date.weekday() == 6 and _norm(getattr(weekend, "sunday", None)) == 'off':
                is_weekend = True

        # Holiday row (to classify public vs optional)
        hrow = Holiday.query.filter_by(date=cur_date).first()
        is_public_holiday = bool(hrow and _holiday_is_public(hrow))
        is_optional_holiday = bool(hrow and _holiday_is_optional(hrow))

        record = attendance_map.get(cur_date)
        if not record:
            record = Attendance(employee_id=emp.id, date=cur_date)
            db.session.add(record)
            attendance_map[cur_date] = record
        
        # ✅ If already marked Leave (manual/system), don't overwrite it
        if _norm(record.remarks) == 'leave' or _norm(record.status) in ('leave', 'paid_leave'):
            cur_date += timedelta(days=1)
            processed_days += 1
            continue

        # --- 1) Weekends: system Weekend ---
        if is_weekend:
            record.check_in_time = None
            record.check_out_time = None
            record.working_hours = 0
            record.remarks = 'Weekend'
            record.status = 'weekend'
            record.source = 'System'
            cur_date += timedelta(days=1)
            processed_days += 1
            continue

        # --- 2) Public Holiday ---
        if is_public_holiday:
            record.check_in_time = None
            record.check_out_time = None
            record.working_hours = 0
            record.remarks = 'Holiday'
            record.status = 'holiday'
            record.source = 'System'
            cur_date += timedelta(days=1)
            processed_days += 1
            continue

        # --- 3) Optional holiday (RH) ---
        if is_optional_holiday:
            if cur_date in approved_rh_dates:
                record.check_in_time = None
                record.check_out_time = None
                record.working_hours = 0
                record.remarks = 'Holiday'
                record.status = 'holiday'
                record.source = 'System'
                cur_date += timedelta(days=1)
                processed_days += 1
                continue

        # --- 4) Non-RH approved leave ---
        if cur_date in approved_leave_dates_non_rh:
            record.check_in_time = None
            record.check_out_time = None
            record.working_hours = 0
            record.remarks = 'Leave'
            record.status = 'leave'
            record.source = 'System'
            cur_date += timedelta(days=1)
            processed_days += 1
            continue

        # ✅ Never overwrite manual updates
        if _norm(record.source) in ('manual', 'user'):
            cur_date += timedelta(days=1)
            processed_days += 1
            continue

        # --- 5) Normal working day → unified engine ---
        engine_att = recalc_attendance_for_day(emp, cur_date)
        attendance_map[cur_date] = engine_att
        processed_days += 1

        cur_date += timedelta(days=1)

    db.session.commit()
    return {
        "processed": True,
        "reason": "ok",
        "emp_id": emp.id,
        "start": emp_start_date,
        "end": end_date,
        "days": processed_days,
    }

# =========================
# Leave type normalization
# =========================

def _normalize_leave_type(value) -> str:
    if value is None:
        return ""
    if hasattr(value, "__dict__"):
        for attr in ("code", "name", "type", "category", "short_code", "key", "title", "label"):
            v = getattr(value, attr, None)
            if isinstance(v, str) and v.strip():
                return v.strip().upper()
        return ""
    if isinstance(value, str):
        return value.strip().upper()
    return ""

def _is_rh_leave(value) -> bool:
    lt = _normalize_leave_type(value)
    if not lt:
        return False
    RH_ALIASES = {
        "RH", "R H", "RESTRICTED", "RESTRICTED HOLIDAY",
        "OPTIONAL", "OPTIONAL HOLIDAY", "RESTRICTED/OPTIONAL"
    }
    return lt in RH_ALIASES or ("RESTRICTED" in lt) or ("OPTIONAL" in lt)

# =========================
# Misc helpers
# =========================

def resolve_employee_id_for_user(user):
    eid = getattr(user, "employee_id", None)
    if eid:
        return eid
    emp_rel = getattr(user, "employee", None)
    if emp_rel and getattr(emp_rel, "id", None):
        return emp_rel.id
    emp = Employee.query.filter_by(employee_code=getattr(user, "id", None)).first()
    if emp:
        return emp.id
    if getattr(user, "email", None):
        emp = Employee.query.filter_by(email=user.email).first()
        if emp:
            return emp.id
    return None

def _norm(s):
    return (s or "").strip().lower()

def _holiday_is_optional(h) -> bool:
    if hasattr(h, "is_optional"):
        return bool(getattr(h, "is_optional"))
    t = (getattr(h, "type", None) or getattr(h, "category", None) or "").strip().upper()
    return any(k in t for k in ("RH", "RESTRICTED", "OPTIONAL"))

def _holiday_is_public(h) -> bool:
    if hasattr(h, "is_optional"):
        return not bool(getattr(h, "is_optional"))
    return not _holiday_is_optional(h)

def saturday_mode(settings, day):
    if not settings:
        return 'working'
    week_number = (day.day - 1) // 7 + 1
    key = ["first", "second", "third", "fourth", "fifth"][week_number - 1]
    raw = getattr(settings, f"{key}_saturday", None)
    v = _norm(raw)
    if v in ('off', 'holiday', 'weekend'):
        return 'off'
    if v in ('half-day', 'halfday', 'half'):
        return 'half-day'
    return 'working'

def is_sunday_off(settings):
    return _norm(getattr(settings, "sunday", None)) in ('off', 'holiday', 'weekend')

# =========================
# Blueprint
# =========================

attendance_bp = Blueprint('attendance', __name__, url_prefix='/attendance')

# =========================
# Routes
# =========================

@attendance_bp.route('/mark', methods=['POST'])
@login_required
def mark_attendance():
    date_str = request.form['date']
    att_date = datetime.strptime(date_str, '%Y-%m-%d').date()

    employee_id = resolve_employee_id_for_user(current_user)
    if not employee_id:
        flash("❌ Unable to resolve employee.", "danger")
        return redirect(url_for('attendance.view_calendar'))

    attendance = Attendance.query.filter_by(employee_id=employee_id, date=att_date).first()
    if not attendance:
        attendance = Attendance(employee_id=employee_id, date=att_date)
        db.session.add(attendance)

    attendance.check_in_time = datetime.now().time()
    attendance.updated_on = datetime.now()
    db.session.commit()

    flash(f'Attendance updated for {att_date}!', 'success')
    return redirect(url_for('attendance.view_calendar'))

@attendance_bp.route('/calendar', methods=['GET'])
@login_required
def view_calendar():
    today = datetime.today()
    fy_start = today.year if today.month >= 4 else today.year - 1
    fy_end = fy_start + 1

    self_employee_id = resolve_employee_id_for_user(current_user)
    if not self_employee_id:
        flash("❌ Unable to resolve employee for calendar.", "danger")
        return redirect(url_for('dashboard'))

    events = get_calendar_events(self_employee_id, fy_start, fy_end)
    current_month = today.month

    is_admin = (getattr(current_user, "role", None) and
                getattr(current_user.role, "name", "").lower() == 'admin')

    employees = []
    if is_admin:
        employees = (Employee.query
                     .filter_by(status='active')
                     .order_by(Employee.full_name.asc())
                     .all())

    return render_template(
        'attendance/calendar.html',
        events=events,
        year=today.year,
        month=current_month,
        fy_start=fy_start,
        fy_end=fy_end,
        employees=employees,
        self_employee_id=self_employee_id
    )

# =========================
# Calendar events
# =========================

def get_calendar_events(employee_id, fy_start, fy_end):
    events = []
    fy_start_date = date(fy_start, 4, 1)
    fy_end_date = date(fy_end, 3, 31)

    emp = Employee.query.get(employee_id)
    min_present, min_half = _display_thresholds_for_employee(emp) if emp else (8.0, 4.0)

    # ---- Build approved RH dates (for this employee) ----
    approved_rh_dates = set()
    rh_leaves = (
        LeaveRequest.query
        .filter(
            LeaveRequest.employee_id == employee_id,
            LeaveRequest.status == 'final_approved',
            LeaveRequest.start_date <= fy_end_date,
            LeaveRequest.end_date >= fy_start_date,
        )
        .all()
    )
    for lr in rh_leaves:
        if not _is_rh_leave(getattr(lr, "leave_type", None)):
            continue
        d = max(lr.start_date, fy_start_date)
        d_end = min(lr.end_date, fy_end_date)
        while d <= d_end:
            approved_rh_dates.add(d)
            d += timedelta(days=1)

    # ---- Holidays ----
    holidays = Holiday.query.filter(
        Holiday.date >= fy_start_date,
        Holiday.date <= fy_end_date
    ).all()

    public_holiday_dates = set()

    for h in holidays:
        if _holiday_is_public(h):
            public_holiday_dates.add(h.date)
            events.append({
                'title': "🎉",
                'start': h.date.isoformat(),
                'color': '#ffc107',
                'extendedProps': {'tooltip': f"Holiday: {getattr(h, 'name', 'Holiday')}"}
            })

    for h in holidays:
        if _holiday_is_optional(h) and h.date in approved_rh_dates:
            events.append({
                'title': "🟠",
                'start': h.date.isoformat(),
                'color': '#F39C12',
                'extendedProps': {'tooltip': f"Holiday (RH Approved): {getattr(h, 'name', 'Holiday')}"}
            })

    # ---- Non-RH approved leaves ----
    leave_dates_non_rh = set()
    leaves = (LeaveRequest.query
              .filter_by(employee_id=employee_id, status='final_approved')
              .filter(LeaveRequest.start_date <= fy_end_date, LeaveRequest.end_date >= fy_start_date)
              .all())
    for leave in leaves:
        is_rh = _is_rh_leave(getattr(leave, "leave_type", None))
        d0 = max(leave.start_date, fy_start_date)
        d1 = min(leave.end_date, fy_end_date)
        d = d0
        while d <= d1:
            if not is_rh:
                leave_dates_non_rh.add(d)
                events.append({
                    'title': "✈️",
                    'start': d.isoformat(),
                    'color': '#6FA8DC',
                    'extendedProps': {'tooltip': "Approved Leave"}
                })
            d += timedelta(days=1)

    # ---- Weekend settings ----
    weekend_settings = WeekendSettings.query.order_by(WeekendSettings.updated_on.desc()).first()
    weekend_dates = set()

    def get_saturday_status(settings, day):
        if not settings:
            return None
        week_number = (day.day - 1) // 7 + 1
        slot = ["first", "second", "third", "fourth", "fifth"][week_number - 1]
        return getattr(settings, f"{slot}_saturday", None)

    d = fy_start_date
    while d <= fy_end_date:
        if d.weekday() == 5:
            sat_status = _norm(get_saturday_status(weekend_settings, d))
            if sat_status == 'off':
                weekend_dates.add(d)
                events.append({
                    'title': '🎓',
                    'start': d.isoformat(),
                    'color': "#AEABAB",
                    'extendedProps': {'tooltip': f"{((d.day - 1)//7)+1} Saturday Off"}
                })
            elif sat_status in ('half-day', 'halfday', 'half'):
                weekend_dates.add(d)
                events.append({
                    'title': '🌓',
                    'start': d.isoformat(),
                    'color': "#F7DC6F",
                    'extendedProps': {'tooltip': f"{((d.day - 1)//7)+1} Saturday Half Day"}
                })
        elif d.weekday() == 6 and weekend_settings and _norm(weekend_settings.sunday) == 'off':
            weekend_dates.add(d)
            events.append({
                'title': '🎓',
                'start': d.isoformat(),
                'color': "#AEABAB",
                'extendedProps': {'tooltip': 'Sunday Off'}
            })
        d += timedelta(days=1)

    # ---- Attendance ----
    attendance_records = (Attendance.query
                          .filter_by(employee_id=employee_id)
                          .filter(Attendance.date >= fy_start_date, Attendance.date <= fy_end_date)
                          .all())

    for a in attendance_records:
        if (a.date in public_holiday_dates
                or a.date in approved_rh_dates
                or a.date in leave_dates_non_rh):
            continue

        checkin = a.check_in_time.strftime('%H:%M') if a.check_in_time else '--'
        checkout = a.check_out_time.strftime('%H:%M') if a.check_out_time else '--'

        desktime_log = DesktimeLog.query.filter_by(employee_id=employee_id, date=a.date).first()
        dt_checkin = desktime_log.first_activity.strftime('%H:%M') if desktime_log and desktime_log.first_activity else '--'
        dt_checkout = desktime_log.last_activity.strftime('%H:%M') if desktime_log and desktime_log.last_activity else '--'
        dt_hours = round(desktime_log.productive_hours, 2) if desktime_log and desktime_log.productive_hours else '--'

        tooltip = f"""
        Check-in: {checkin},<br>
        Check-out: {checkout},<br>
        Source: {a.source or '--'},<br>
        Desktime In: {dt_checkin},<br>
        Desktime Out: {dt_checkout},<br>
        Productive Hours: {dt_hours}
        """.strip()

        # ✅ FIX: compute what to SHOW from hours if possible
        status = _compute_effective_status_for_display(
            a,
            min_present=min_present,
            min_half=min_half,
            weekend_dates=weekend_dates
        )

        if status == 'Present':
            icon, color = '✅', "#a8e5b6"
        elif status == 'Half Day':
            icon, color = '🌓', "#e4c394"
        elif status == 'LWP':
            icon, color = '❌', "#d18189"
        elif status == 'Holiday':
            icon, color = '🎉', "#ffc107"
        elif status == 'Leave':
            icon, color = '✈️', "#6FA8DC"
        elif status == 'Weekend':
            icon, color = '🎓', "#AEABAB"
        else:
            icon, color = '❓', '#adb5bd'

        events.append({
            'title': icon,
            'start': a.date.isoformat(),
            'color': color,
            'extendedProps': {'tooltip': tooltip}
        })

    return events


@attendance_bp.route('/process-monthly', methods=['POST'], endpoint='process_monthly_attendance')
@login_required
def process_monthly_attendance():
    selected_month_str = request.form.get('selected_month')
    if not selected_month_str:
        flash("❌ No month selected", "danger")
        return redirect(url_for('attendance.view_calendar'))

    try:
        year, month = map(int, selected_month_str.split('-'))
    except Exception:
        flash("❌ Invalid month format, expected YYYY-MM", "danger")
        return redirect(url_for('attendance.view_calendar'))

    is_admin = (getattr(current_user, "role", None) and
                getattr(current_user.role, "name", "").lower() == 'admin')
    employee_id = request.form.get('employee_id', type=int) if is_admin else None
    if not employee_id:
        employee_id = resolve_employee_id_for_user(current_user)

    if not employee_id:
        flash("❌ No employee selected", "danger")
        return redirect(url_for('attendance.view_calendar'))

    emp = Employee.query.get(employee_id)
    if not emp or emp.status != 'active':
        flash("❌ Employee not found or inactive", "danger")
        return redirect(url_for('attendance.view_calendar'))

    result = process_attendance_for_employee_month(emp, year, month)

    if not result["processed"] and result["reason"] == "joins_after_range":
        flash("ℹ️ No days to process (joins after selected range).", "info")
    else:
        flash(f"✅ Attendance processed for {emp.full_name} for {selected_month_str}.", "success")

    return redirect(url_for('attendance.view_calendar'))

@attendance_bp.route('/weekend-settings', methods=['GET', 'POST'])
@login_required
def weekend_settings():
    settings = WeekendSettings.query.order_by(WeekendSettings.updated_on.desc()).first()

    if not settings:
        settings = WeekendSettings()
        db.session.add(settings)
        db.session.commit()

    if request.method == 'POST':
        settings.first_saturday = request.form.get('first_saturday')
        settings.second_saturday = request.form.get('second_saturday')
        settings.third_saturday = request.form.get('third_saturday')
        settings.fourth_saturday = request.form.get('fourth_saturday')
        settings.fifth_saturday = request.form.get('fifth_saturday')
        settings.sunday = request.form.get('sunday')
        settings.updated_on = datetime.utcnow()
        db.session.commit()
        flash('✅ Weekend settings updated!', 'success')
        return redirect(url_for('attendance.weekend_settings'))

    return render_template('attendance/weekend_settings.html', settings=settings)

@attendance_bp.route('/apply-month-calendar', methods=['POST'])
@login_required
def apply_month_calendar():
    """
    Body JSON: {"month": "YYYY-MM"}
    Applies Holidays, Weekends (off), and Half-day Saturdays to ATTENDANCE
    """
    role_name = _norm(getattr(getattr(current_user, 'role', None), 'name', ''))
    if not (getattr(current_user, "is_admin", False) or role_name in ('admin', 'hr admin', 'hr manager')):
        return jsonify({"error": "Forbidden"}), 403

    payload = request.get_json(silent=True) or {}
    month_str = payload.get("month")
    if not month_str:
        return jsonify({"error": "Missing 'month' (YYYY-MM)"}), 400

    try:
        year, month = map(int, month_str.split('-'))
    except Exception:
        return jsonify({"error": "Invalid 'month' format, expected YYYY-MM"}), 400

    start_date = date(year, month, 1)
    end_date   = date(year, month, calendar.monthrange(year, month)[1])

    weekend = WeekendSettings.query.order_by(WeekendSettings.updated_on.desc()).first()

    holiday_map = {
        h.date: h for h in Holiday.query
        .filter(Holiday.date >= start_date, Holiday.date <= end_date)
        .all()
    }

    emp_rows = (
        db.session.query(Employee.id, Employee.date_of_joining, Employee.exit_date)
        .filter(
            func.coalesce(Employee.date_of_joining, start_date) <= end_date,
            or_(Employee.exit_date == None, Employee.exit_date >= start_date)
        )
        .all()
    )
    employees = [{"id": eid, "start": doj or start_date, "end": exd or end_date}
                 for (eid, doj, exd) in emp_rows]
    if not employees:
        return jsonify({"ok": True, "month": month_str, "employees": 0, "rows_created": 0, "rows_updated": 0}), 200

    existing = {
        (att.employee_id, att.date): att
        for att in Attendance.query
            .filter(Attendance.date >= start_date, Attendance.date <= end_date)
            .filter(Attendance.employee_id.in_([e["id"] for e in employees]))
            .all()
    }

    from collections import defaultdict
    emp_rh_dates = defaultdict(set)
    lr_rows = (LeaveRequest.query
        .filter(LeaveRequest.status=='final_approved',
                LeaveRequest.start_date <= end_date,
                LeaveRequest.end_date >= start_date)
        .all())
    for lr in lr_rows:
        if not _is_rh_leave(getattr(lr, "leave_type", None)):
            continue
        d0 = max(lr.start_date, start_date)
        d1 = min(lr.end_date,   end_date)
        cur = d0
        while cur <= d1:
            emp_rh_dates[lr.employee_id].add(cur)
            cur += timedelta(days=1)

    updated = 0
    created = 0

    d = start_date
    while d <= end_date:
        hrow = holiday_map.get(d)
        is_public = bool(hrow and _holiday_is_public(hrow))
        is_optional = bool(hrow and _holiday_is_optional(hrow))

        wknd_flag = False
        halfday_flag = False
        if weekend:
            if d.weekday() == 5:
                mode = saturday_mode(weekend, d)
                wknd_flag   = (mode == 'off')
                halfday_flag = (mode == 'half-day')
            elif d.weekday() == 6 and is_sunday_off(weekend):
                wknd_flag = True

        for e in employees:
            eid = e["id"]
            if d < e["start"] or d > e["end"]:
                continue

            att = existing.get((eid, d))
            if not att:
                att = Attendance(employee_id=eid, date=d)
                db.session.add(att)
                existing[(eid, d)] = att
                created += 1

            current = _norm(att.remarks)
            cur_status = _norm(att.status)
            source = _norm(att.source)

            # ✅ HARD SKIP:
            # 1. Any manual update
            # 2. Any leave (system or manual)
            # 3. Present marked manually
            if source in ('manual', 'user'):
                continue

            if current in ('present', 'leave') or cur_status in ('leave', 'paid_leave'):
                continue

            if hrow:
                if is_public:
                    if current in {'', None, 'holiday', 'weekend', 'half day', 'lwp', 'absent', 'unknown'}:
                        att.remarks = 'Holiday'
                        att.status = 'holiday'
                        att.source = 'System'
                        att.working_hours = 0
                        updated += 1
                    continue
                else:
                    if d in emp_rh_dates.get(eid, set()):
                        if current in {'', None, 'holiday', 'weekend', 'half day', 'lwp', 'absent', 'unknown'}:
                            att.remarks = 'Holiday'
                            att.status = 'holiday'
                            att.source = 'System'
                            att.working_hours = 0
                            updated += 1
                        continue

            if wknd_flag:
                if current in {'', None, 'weekend', 'half day', 'lwp', 'absent', 'unknown'}:
                    att.remarks = 'Weekend'
                    att.status = 'weekend'
                    att.source = 'System'
                    att.working_hours = 0
                    updated += 1
                continue

            if halfday_flag:
                if current in {'', None, 'half day', 'lwp', 'absent', 'unknown'}:
                    att.remarks = 'Half Day'
                    att.status = 'half_day'
                    att.source = att.source or 'System'
                    updated += 1
                continue

            if not hrow and not wknd_flag and not halfday_flag:
                # ✅ Do not touch manual or leave rows
                if source in ('manual', 'user'):
                    continue

                if (
                    current in {'weekend', 'half day'}
                    and current not in {'leave'}
                    and cur_status not in {'leave', 'paid_leave'}
                ):
                    att.remarks = ''
                    att.status = ''
                    att.source = att.source or 'System'
                    updated += 1

        d += timedelta(days=1)

    db.session.commit()
    return jsonify({
        "ok": True,
        "month": month_str,
        "employees": len(employees),
        "rows_created": created,
        "rows_updated": updated
    }), 200