<?php

namespace App\Controllers;

use App\Core\AuditLog;
use App\Core\Auth;
use App\Core\Database;
use App\Core\Permission;
use App\Core\Router;
use App\Models\KpiScheduling;
use App\Models\Lookup;
use App\Models\UserManager;

class KpiSchedulingController
{
    /** Super admin OR sara.lasher — both have full KPI oversight across the whole hierarchy. */
    private function isFullOversight(array $user): bool
    {
        return !empty($user['is_super_admin'])
            || ($user['user_type'] === 'manager' && ($user['username'] ?? '') === 'sara.lasher');
    }

    private function schedulingDeptId(): int
    {
        return KpiScheduling::departmentIdFor('scheduling') ?? 0;
    }

    public function index(): void
    {
        $user = Permission::requireLogin();

        // César has his own separate Operations Manager KPI form
        if (($user['username'] ?? '') === 'cesar.saona') {
            Router::redirect('/projects/cesar-kpi');
        }

        if ($user['is_super_admin']) {
            Router::redirect('/projects/kpi/company');
        }
        // sara.lasher is the top-level manager with full KPI oversight
        if ($user['user_type'] === 'manager' && ($user['username'] ?? '') === 'sara.lasher') {
            Router::redirect('/projects/kpi/company');
        }
        if (KpiScheduling::canSubmitEntries($user)) {
            Router::redirect('/projects/kpi/my-records');
        }

        $reviewable = $user['user_type'] === 'manager' ? KpiScheduling::reviewableSubmitters($user['user_id']) : [];
        if (!empty($reviewable)) {
            Router::redirect('/projects/kpi/review');
        }

        http_response_code(403);
        echo 'KPI Tracker is not available for your account yet.';
    }

    public function myRecords(): void
    {
        $user = Permission::requireLogin();
        if (!KpiScheduling::canSubmitEntries($user)) { http_response_code(403); echo 'Not authorized.'; return; }

        $filters = $this->filtersFromRequest();
        $records = KpiScheduling::forSubmitter($user['user_type'], $user['user_id'], $filters);

        $flash = $_SESSION['flash'] ?? null;
        unset($_SESSION['flash']);

        require __DIR__ . '/../Views/kpi/my_records.php';
    }

    public function entryForm(): void
    {
        $user = Permission::requireLogin();
        if (!KpiScheduling::canSubmitEntries($user)) { http_response_code(403); echo 'Not authorized.'; return; }

        $id = (int)($_GET['id'] ?? 0);
        $entry = $id ? KpiScheduling::find($id) : null;

        if ($entry && (int)$entry['submitter_id'] !== $user['user_id']) { http_response_code(403); echo 'Not your record.'; return; }
        if ($entry && !in_array($entry['status'], ['draft', 'returned'], true)) {
            Router::redirect('/projects/kpi/view?id=' . $id);
        }

        // For a brand-new blank form, default to the next week that doesn't
        // already have a non-editable entry, so "+ New" doesn't immediately
        // collide with an already-submitted week. Still always a blank form.
        $defaultWeekEnding = '';
        if (!$entry) {
            for ($i = 0; $i < 12; $i++) {
                $existing = KpiScheduling::findDraftOrEntry($user['user_type'], $user['user_id'], $defaultWeekEnding);
                if (!$existing || in_array($existing['status'], ['draft', 'returned'], true)) break;
                $defaultWeekEnding = date('Y-m-d', strtotime($defaultWeekEnding . ' +7 days'));
            }
        }

        $allPeopleNames = $this->allPeopleNames();

        require __DIR__ . '/../Views/kpi/entry_form.php';
    }

    private function allPeopleNames(): array
    {
        $db = Database::connection();
        $names = $db->query("SELECT name FROM employees WHERE name IS NOT NULL ORDER BY name")->fetchAll(\PDO::FETCH_COLUMN);
        return array_values(array_unique($names));
    }

    public function saveDraft(): void
    {
        $user = Permission::requireLogin();
        if (!KpiScheduling::canSubmitEntries($user)) { http_response_code(403); echo 'Not authorized.'; return; }

        $isModal = !empty($_GET['modal']);

        try {
            $id = KpiScheduling::saveDraft(
                $user, $this->schedulingDeptId(), $this->schedulingProjectId(),
                $_POST['week_ending'] ?? date('Y-m-d'), $_POST, (int)($_POST['entry_id'] ?: 0) ?: null
            );
        } catch (\RuntimeException $e) {
            $_SESSION['flash'] = ['type' => 'error', 'msg' => $e->getMessage()];
            Router::redirect('/projects/kpi/my-records');
            return;
        }

        $_SESSION['flash'] = ['type' => 'success', 'msg' => 'Draft saved.'];
        \App\Models\ActivityLog::log($user, 'kpi_draft_saved', 'Saved a draft Weekly KPI for week ending ' . ($_POST['week_ending'] ?? ''), $this->schedulingDeptId(), $this->schedulingProjectId());

        if (!empty($_POST['then_submit'])) {
            KpiScheduling::submit($id, $user);
            $entry = KpiScheduling::find($id);
            \App\Models\ActivityLog::log($user, 'kpi_submitted', 'Submitted Weekly KPI for week ending ' . ($entry['week_ending'] ?? '') . ' for review', $this->schedulingDeptId(), $this->schedulingProjectId());
            // Notify direct supervisor (Cesar) that a KPI is ready to review
            self::notifyDirectSupervisor($user['user_type'], $user['user_id'],
                $user['name'] . ' submitted a Weekly KPI for review',
                '/projects/kpi/review');
            $_SESSION['flash'] = ['type' => 'success', 'msg' => 'Weekly KPI submitted for review.'];
            Router::redirect($isModal ? '/projects/kpi/modal-done?msg=submitted' : '/projects/kpi/my-records');
            return;
        }

        Router::redirect($isModal ? '/projects/kpi/modal-done?msg=saved' : '/projects/kpi/entry?id=' . $id);
    }

    public function modalDone(): void
    {
        Permission::requireLogin();
        $msg   = $_GET['msg'] ?? 'saved';
        $flash = $_SESSION['flash']['msg'] ?? '';
        unset($_SESSION['flash']);
        ?><!DOCTYPE html><html><head>
        <link rel="stylesheet" href="/assets/css/app.css?v=<?= defined('ASSET_VERSION') ? ASSET_VERSION : time() ?>">
        </head><body style="background:#fff;padding:40px 32px;font-family:inherit;">
        <div style="text-align:center;">
          <div style="font-size:32px;margin-bottom:12px;"><?= $msg==='submitted'?'✅':'💾' ?></div>
          <div style="font-size:16px;font-weight:700;color:var(--navy-900);margin-bottom:6px;">
            <?= $msg==='submitted' ? 'KPI Submitted!' : 'Draft Saved!' ?>
          </div>
          <?php if ($flash): ?><div style="font-size:13px;color:var(--text-muted);"><?= htmlspecialchars($flash) ?></div><?php endif; ?>
        </div>
        <script>
          try { window.parent.postMessage({type:'kpi-<?= $msg==='submitted'?'submitted':'saved' ?>'}, '*'); } catch(e) {}
          setTimeout(function() {
            try { window.parent.location.href = '/projects/kpi/my-records'; } catch(e) {}
          }, 800);
        </script>
        </body></html>
        <?php
    }

    public function submit(): void
    {
        $user = Permission::requireLogin();
        if (!KpiScheduling::canSubmitEntries($user)) { http_response_code(403); echo 'Not authorized.'; return; }

        $id = (int)($_POST['id'] ?? 0);
        $entry = KpiScheduling::find($id);
        if (!$entry || (int)$entry['submitter_id'] !== $user['user_id']) { http_response_code(403); echo 'Not your record.'; return; }

        KpiScheduling::submit($id, $user);
        \App\Models\ActivityLog::log($user, 'kpi_submitted', 'Submitted Weekly KPI for week ending ' . $entry['week_ending'] . ' for review', (int)$entry['department_id'], (int)($entry['project_id'] ?? 0) ?: null);
        $_SESSION['flash'] = ['type' => 'success', 'msg' => 'Weekly KPI submitted for review.'];
        Router::redirect('/projects/kpi/my-records');
    }

    public function view(): void
    {
        $user = Permission::requireLogin();
        $id = (int)($_GET['id'] ?? 0);
        $entry = KpiScheduling::find($id);
        if (!$entry) { http_response_code(404); echo 'Not found.'; return; }

        $isOwner = $entry['submitter_type'] === $user['user_type'] && (int)$entry['submitter_id'] === $user['user_id'];
        $canReview = KpiScheduling::canReview($user, $entry['submitter_type'], (int)$entry['submitter_id']);

        if (!$isOwner && !$canReview) { http_response_code(403); echo 'Not authorized to view this record.'; return; }

        $history = KpiScheduling::auditHistory($id);
        $flash = $_SESSION['flash'] ?? null;
        unset($_SESSION['flash']);

        require __DIR__ . '/../Views/kpi/record_detail.php';
    }

    public function reviewQueue(): void
    {
        $user = Permission::requireLogin();

        if ($this->isFullOversight($user)) {
            // Sara (and super admin) sees ALL scheduling KPI entries, across
            // the whole hierarchy — not just her direct reports.
            $filters = $this->filtersFromRequest();
            $records = KpiScheduling::allEntries($filters);
            require __DIR__ . '/../Views/kpi/review_queue.php';
            return;
        }

        if ($user['user_type'] !== 'manager') { http_response_code(403); echo 'Not authorized.'; return; }

        $submitters = KpiScheduling::reviewableSubmitters($user['user_id']);
        if (empty($submitters)) { http_response_code(403); echo 'No one reports to you for KPI review yet.'; return; }

        $ids = array_map(fn($s) => (int)$s['id'], $submitters);
        $filters = $this->filtersFromRequest();
        $records = KpiScheduling::forSubmitterIds($ids, 'manager', $filters);

        require __DIR__ . '/../Views/kpi/review_queue.php';
    }

    public function reviewAction(): void
    {
        $user = Permission::requireLogin();
        $id = (int)($_POST['id'] ?? 0);
        $entry = KpiScheduling::find($id);
        if (!$entry || !KpiScheduling::canReview($user, $entry['submitter_type'], (int)$entry['submitter_id'])) {
            http_response_code(403); echo 'Not authorized.'; return;
        }

        $action      = $_POST['action'] ?? '';
        $submitterId = (int)$entry['submitter_id'];
        $submitterType = $entry['submitter_type'];
        $weekEnd     = $entry['week_ending'];
        $deptId      = (int)$entry['department_id'];
        $projId      = (int)($entry['project_id'] ?? 0) ?: null;
        $kpiLink     = '/projects/kpi/view?id=' . $id;

        if ($action === 'comment') {
            $comment = trim($_POST['comment'] ?? '');
            KpiScheduling::review($id, $user, $comment);
            \App\Models\ActivityLog::log($user, 'kpi_comment_added', 'Commented on ' . $entry['submitter_name'] . "'s Weekly KPI (week ending {$weekEnd}): \"" . mb_strimwidth($comment, 0, 120, '...') . '"', $deptId, $projId);
            // Notify the employee their KPI has a new comment
            \App\Models\Notification::push($submitterType, $submitterId,
                $user['name'] . ' commented on your KPI',
                'Week ending ' . $weekEnd . ': "' . mb_strimwidth($comment, 0, 120, '...') . '"',
                $kpiLink);
            $_SESSION['flash'] = ['type' => 'success', 'msg' => 'Review comment saved.'];

        } elseif ($action === 'approve') {
            $score   = (int)($_POST['review_score'] ?? 0);
            $comment = trim($_POST['review_comment_approve'] ?? '');
            KpiScheduling::approve($id, $user, $score, $comment);
            $scoreNote = $score ? " Score: {$score}/10." : '';
            \App\Models\ActivityLog::log($user, 'kpi_approved', 'Approved and locked ' . $entry['submitter_name'] . "'s Weekly KPI (week ending {$weekEnd}).{$scoreNote}", $deptId, $projId);
            // Notify the employee their KPI was approved with score
            \App\Models\Notification::push($submitterType, $submitterId,
                'Your KPI was approved' . ($score ? " — Score: {$score}/10" : ''),
                'Week ending ' . $weekEnd . ($comment ? ': "' . mb_strimwidth($comment, 0, 120, '...') . '"' : ''),
                $kpiLink);
            // Also notify Sara (all full-oversight managers) so it shows on her activity
            self::notifySaraHierarchy($user, $entry['submitter_name'] . "'s Weekly KPI (week ending {$weekEnd}) approved.{$scoreNote}", $kpiLink);
            $_SESSION['flash'] = ['type' => 'success', 'msg' => 'KPI approved and locked.'];

        } elseif ($action === 'return') {
            $reason = trim($_POST['return_reason'] ?? '');
            if ($reason === '') {
                $_SESSION['flash'] = ['type' => 'error', 'msg' => 'A return reason is required.'];
                Router::redirect('/projects/kpi/view?id=' . $id);
            }
            KpiScheduling::return_($id, $user, $reason);
            \App\Models\ActivityLog::log($user, 'kpi_returned', 'Returned ' . $entry['submitter_name'] . "'s Weekly KPI for edits (week ending {$weekEnd}): \"" . mb_strimwidth($reason, 0, 120, '...') . '"', $deptId, $projId);
            // Notify the employee their KPI was returned
            \App\Models\Notification::push($submitterType, $submitterId,
                'Your KPI was returned for edits',
                'Week ending ' . $weekEnd . ': "' . mb_strimwidth($reason, 0, 120, '...') . '"',
                '/projects/kpi/entry?id=' . $id);
            $_SESSION['flash'] = ['type' => 'success', 'msg' => 'KPI returned to submitter for edits.'];
            self::notifySaraHierarchy($user, $entry['submitter_name'] . "'s Weekly KPI (week ending {$weekEnd}) returned for edits.", $kpiLink);

        } elseif ($action === 'revise_score') {
            $newScore   = (int)($_POST['new_score'] ?? 0);
            $newComment = trim($_POST['new_comment'] ?? '');
            $reason     = trim($_POST['revise_reason'] ?? '');
            if (!$newScore || !$reason) {
                $_SESSION['flash'] = ['type' => 'error', 'msg' => 'New score and reason are required to revise.'];
            } else {
                KpiScheduling::reviseScore($id, $user, $newScore, $newComment, $reason);
                \App\Models\ActivityLog::log($user, 'kpi_score_revised', 'Revised score on ' . $entry['submitter_name'] . "'s Weekly KPI (week ending {$weekEnd}) to {$newScore}/10. Reason: " . mb_strimwidth($reason, 0, 120, '...'), $deptId, $projId);
                // Notify the employee their score was revised
                \App\Models\Notification::push($submitterType, $submitterId,
                    'Your KPI score was revised to ' . $newScore . '/10',
                    'Week ending ' . $weekEnd . ($newComment ? ': "' . mb_strimwidth($newComment, 0, 120, '...') . '"' : ''),
                    $kpiLink);
                $_SESSION['flash'] = ['type' => 'success', 'msg' => 'Score revised.'];
                self::notifySaraHierarchy($user, $entry['submitter_name'] . "'s Weekly KPI (week ending {$weekEnd}) score revised to {$newScore}/10.", $kpiLink);
            }
        }

        $isModal = !empty($_POST['modal']);
        Router::redirect('/projects/kpi/view?id=' . $id . ($isModal ? '&modal=1' : ''));
    }

    /**
     * Append a message to a KPI record's review discussion thread.
     *
     * Either side may post: the submitter writes their explanation in
     * response to the reviewer's comment, and the reviewer can reply back.
     * Whoever did NOT write the message gets a notification.
     *
     * This action is strictly conversational. It never touches
     * kpi_scheduling_entries — no status change, no score change, no
     * unlocking. A locked record stays locked.
     */
    public function postReply(): void
    {
        $user  = Permission::requireLogin();
        $id    = (int)($_POST['id'] ?? 0);
        $entry = KpiScheduling::find($id);

        if (!$entry) { http_response_code(404); echo 'Not found.'; return; }

        $isOwner   = $entry['submitter_type'] === $user['user_type']
                     && (int)$entry['submitter_id'] === $user['user_id'];
        $canReview = KpiScheduling::canReview($user, $entry['submitter_type'], (int)$entry['submitter_id']);

        // Viewing the thread is allowed for the owner and for reviewers/senior
        // oversight. POSTING a response is restricted to the KPI owner only —
        // reviewers are view-only once they have left their review comment.
        // Enforced here as well as in the view, so hiding the box in the UI
        // cannot be bypassed by submitting the form directly.
        if (!$isOwner && !$canReview) { http_response_code(403); echo 'Not authorized.'; return; }
        if (!$isOwner) { http_response_code(403); echo 'Only the owner of this KPI record can post a response.'; return; }

        $isModal = !empty($_POST['modal']);
        $body    = trim($_POST['reply_body'] ?? '');

        if ($body === '') {
            $_SESSION['flash'] = ['type' => 'error', 'msg' => 'Please write a response before saving.'];
            Router::redirect('/projects/kpi/view?id=' . $id . ($isModal ? '&modal=1' : ''));
            return;
        }

        // The record owner always posts as 'submitter', even if they also
        // happen to hold review rights elsewhere in the hierarchy.
        $authorRole = $isOwner ? 'submitter' : 'reviewer';

        $msgId = \App\Models\KpiDiscussion::add($id, $user, $authorRole, $body);
        if (!$msgId) {
            $_SESSION['flash'] = ['type' => 'error', 'msg' => 'Sorry — your response could not be saved. Please try again.'];
            Router::redirect('/projects/kpi/view?id=' . $id . ($isModal ? '&modal=1' : ''));
            return;
        }

        $weekEnd = $entry['week_ending'];
        $kpiLink = '/projects/kpi/view?id=' . $id;
        $snippet = mb_strimwidth($body, 0, 120, '...');
        $deptId  = (int)$entry['department_id'];
        $projId  = (int)($entry['project_id'] ?? 0) ?: null;

        if ($authorRole === 'submitter') {
            // Submitter answered the review — notify whoever reviewed it.
            \App\Models\ActivityLog::log(
                $user, 'kpi_reply_added',
                'Responded to the review on their Weekly KPI (week ending ' . $weekEnd . '): "' . $snippet . '"',
                $deptId, $projId
            );
            if (!empty($entry['reviewer_id']) && !empty($entry['reviewer_type'])) {
                \App\Models\Notification::push(
                    $entry['reviewer_type'], (int)$entry['reviewer_id'],
                    $user['name'] . ' responded to your KPI review',
                    'Week ending ' . $weekEnd . ': "' . $snippet . '"',
                    $kpiLink
                );
            } else {
                // No reviewer recorded yet — fall back to the direct supervisor.
                self::notifyDirectSupervisor(
                    $entry['submitter_type'], (int)$entry['submitter_id'],
                    $user['name'] . ' added a response on their Weekly KPI',
                    $kpiLink
                );
            }
        } else {
            // Reviewer replied in the thread — notify the record's owner.
            \App\Models\ActivityLog::log(
                $user, 'kpi_reply_added',
                'Replied on ' . $entry['submitter_name'] . "'s Weekly KPI (week ending " . $weekEnd . '): "' . $snippet . '"',
                $deptId, $projId
            );
            \App\Models\Notification::push(
                $entry['submitter_type'], (int)$entry['submitter_id'],
                $user['name'] . ' replied on your KPI',
                'Week ending ' . $weekEnd . ': "' . $snippet . '"',
                $kpiLink
            );
        }

        $_SESSION['flash'] = ['type' => 'success', 'msg' => 'Your response has been saved.'];
        Router::redirect($kpiLink . ($isModal ? '&modal=1' : ''));
    }

    /**
     * Click-through for a KPI notification chip: records that this user has
     * seen the chip, then forwards to the chip's destination.
     *
     * Mirrors the existing /onboarding/notif-open pattern, including its
     * open-redirect guard. Marking a chip seen only affects that user's own
     * header chips — it never touches KPI records, statuses, or the
     * `notifications` table.
     */
    public function notifOpen(): void
    {
        $user = Permission::requireLogin();

        $key = trim($_GET['key'] ?? '');
        if ($key !== '') {
            \App\Models\KpiNotifSeen::markSeen(
                !empty($user['is_super_admin']) ? 'superadmin' : $user['user_type'],
                (int)$user['user_id'],
                $key
            );
        }

        // Only ever redirect within this app — never an open redirect.
        $to = $_GET['to'] ?? '/projects/kpi/review';
        if ($to === '' || $to[0] !== '/' || str_starts_with($to, '//')) {
            $to = '/projects/kpi/review';
        }

        header('Location: ' . $to);
    }

    public function companyDashboard(): void
    {
        $user = Permission::requireLogin();
        // Allow super admin OR sara.lasher (CEO/top manager with full KPI oversight)
        $isSaraManager = $user['user_type'] === 'manager' && ($user['username'] ?? '') === 'sara.lasher';
        if (!$user['is_super_admin'] && !$isSaraManager) {
            http_response_code(403); echo 'Access denied.'; return;
        }
        $admin = $user;
        $departments = Lookup::departments();
        $schedulingDeptId = $this->schedulingDeptId();
        $activeDeptId = (int)($_GET['dept'] ?? $schedulingDeptId);

        $filters = $this->filtersFromRequest();

        // Always use allEntries() — no department_id filter — because older entries
        // may have been saved with department_id=0 before the dept was fully set up.
        $records = ($activeDeptId === $schedulingDeptId || $activeDeptId === 0)
            ? KpiScheduling::allEntries($filters)
            : [];

        $submitters = [];
        try {
            $stmt = Database::connection()->prepare(
                "SELECT DISTINCT m.id, m.name FROM managers m
                 JOIN kpi_scheduling_entries k ON k.submitter_id = m.id AND k.submitter_type = 'manager'
                 ORDER BY m.name"
            );
            $stmt->execute();
            $submitters = $stmt->fetchAll();
        } catch (\Throwable $e) {}

        // Load Accounts (Jatinder) KPI records for Sara's dashboard
        // AccountsKpi::allEntries uses 'from'/'to' keys, not 'from_date'/'to_date'
        $accountsRecords = [];
        try {
            $accFilters = [];
            if (!empty($filters['status']))    $accFilters['status'] = $filters['status'];
            if (!empty($filters['from_date'])) $accFilters['from']   = $filters['from_date'];
            if (!empty($filters['to_date']))   $accFilters['to']     = $filters['to_date'];
            $accountsRecords = \App\Models\AccountsKpi::allEntries($accFilters);
        } catch (\Throwable $e) {
            // Table may not exist on this DB instance yet — fail silently
            $accountsRecords = [];
        }

        require __DIR__ . '/../Views/kpi/company_dashboard.php';
    }

    public function unlock(): void
    {
        $admin = Permission::requireSuperAdmin();
        $id = (int)($_POST['id'] ?? 0);
        $reason = trim($_POST['unlock_reason'] ?? '');

        if ($reason === '') {
            $_SESSION['flash'] = ['type' => 'error', 'msg' => 'An unlock reason is required.'];
            Router::redirect('/projects/kpi/view?id=' . $id);
        }

        KpiScheduling::unlock($id, $admin, $reason);
        AuditLog::record($admin['user_type'], $admin['user_id'], $admin['name'], 'kpi_entry', (string)$id, 'update', null, ['unlocked' => true, 'reason' => $reason]);
        $entry = KpiScheduling::find($id);
        if ($entry) {
            \App\Models\ActivityLog::log($admin, 'kpi_unlocked', 'Unlocked ' . $entry['submitter_name'] . "'s Weekly KPI (week ending {$entry['week_ending']}) for edits", (int)$entry['department_id'], (int)($entry['project_id'] ?? 0) ?: null);
        }

        $_SESSION['flash'] = ['type' => 'success', 'msg' => 'Record unlocked and returned for edits.'];
        Router::redirect('/projects/kpi/view?id=' . $id);
    }

    public function exportCsv(): void
    {
        $rows = $this->rowsForExport();
        if ($rows === null) { http_response_code(403); echo 'Not authorized.'; return; }
        $labeled = array_map(fn($r) => \App\Core\KpiFieldLabels::toLabeledRow($r), $rows);

        $out = fopen('php://temp', 'r+');
        // UTF-8 BOM so Excel opens accented characters correctly.
        fwrite($out, "\xEF\xBB\xBF");
        if (!empty($labeled)) {
            fputcsv($out, array_keys($labeled[0]));
            foreach ($labeled as $r) fputcsv($out, $r);
        } else {
            fputcsv($out, ['No records match the selected filters.']);
        }
        rewind($out);
        $body = stream_get_contents($out);
        fclose($out);

        header('Content-Type: text/csv; charset=UTF-8');
        header('Content-Disposition: attachment; filename="scheduling_kpi_export.csv"');
        header('Content-Length: ' . strlen($body));
        header('Cache-Control: private, must-revalidate');
        echo $body;
    }

    public function exportXlsx(): void
    {
        $rows = $this->rowsForExport();
        if ($rows === null) { http_response_code(403); echo 'Not authorized.'; return; }
        $labeled = array_map(fn($r) => \App\Core\KpiFieldLabels::toLabeledRow($r), $rows);

        $headers = !empty($labeled) ? array_keys($labeled[0]) : ['No records match the selected filters.'];
        $body = \App\Core\XlsxWriter::write($headers, $labeled, 'Scheduling KPI');

        if (ob_get_level()) ob_end_clean();
        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        header('Content-Disposition: attachment; filename="scheduling_kpi_export.xlsx"');
        header('Content-Length: ' . strlen($body));
        header('Cache-Control: private, must-revalidate');
        echo $body;
    }

    public function exportPdf(): void
    {
        $rows = $this->rowsForExport();
        if ($rows === null) { http_response_code(403); echo 'Not authorized.'; return; }
        $labeled = array_map(fn($r) => \App\Core\KpiFieldLabels::toLabeledRow($r), $rows);

        $body = \App\Core\SimplePdf::tableReport('Hammerhead Staffing — Scheduling Weekly KPI Report', $labeled);

        header('Content-Type: application/pdf');
        header('Content-Disposition: attachment; filename="scheduling_kpi_export.pdf"');
        header('Content-Length: ' . strlen($body));
        echo $body;
    }

    /** Shared permission-scoped row retrieval used by all three export formats. */
    private function rowsForExport(): ?array
    {
        $user = Permission::requireLogin();
        $filters = $this->filtersFromRequest();

        if ($this->isFullOversight($user)) {
            return KpiScheduling::allEntries($filters);
        }
        if (KpiScheduling::canSubmitEntries($user)) {
            return KpiScheduling::forSubmitter($user['user_type'], $user['user_id'], $filters);
        }
        if ($user['user_type'] === 'manager') {
            $submitters = KpiScheduling::reviewableSubmitters($user['user_id']);
            $ids = array_map(fn($s) => (int)$s['id'], $submitters);
            return KpiScheduling::forSubmitterIds($ids, 'manager', $filters);
        }
        return null;
    }

    private function filtersFromRequest(): array
    {
        $filters = [
            'status' => $_GET['status'] ?? '',
            'week_ending' => $_GET['week_ending'] ?? '',
            'from_date' => $_GET['from_date'] ?? '',
            'to_date' => $_GET['to_date'] ?? '',
            'employee_id' => $_GET['employee'] ?? '',
        ];

        $range = $_GET['range'] ?? '';
        $today = new \DateTime();
        if ($range === 'current_week') {
            $monday = (clone $today)->modify('monday this week');
            $filters['from_date'] = $monday->format('Y-m-d');
            $filters['to_date'] = (clone $monday)->modify('+6 days')->format('Y-m-d');
        } elseif ($range === 'previous_week') {
            $monday = (clone $today)->modify('monday this week')->modify('-7 days');
            $filters['from_date'] = $monday->format('Y-m-d');
            $filters['to_date'] = (clone $monday)->modify('+6 days')->format('Y-m-d');
        } elseif ($range === 'current_month') {
            $filters['from_date'] = $today->format('Y-m-01');
            $filters['to_date'] = $today->format('Y-m-t');
        } elseif ($range === 'current_year') {
            $filters['from_date'] = $today->format('Y-01-01');
            $filters['to_date'] = $today->format('Y-12-31');
        }

        return array_filter($filters);
    }

    private function schedulingProjectId(): ?int
    {
        $db = Database::connection();
        $stmt = $db->prepare("SELECT id FROM projects WHERE department_id = :d ORDER BY id LIMIT 1");
        $stmt->execute(['d' => $this->schedulingDeptId()]);
        $r = $stmt->fetch();
        return $r ? (int)$r['id'] : null;
    }

    /**
     * Push a notification to all full-oversight managers (Sara + super admin)
     * who are NOT the actor themselves. Used so Sara always sees when Cesar
     * approves/returns a team member's KPI, or when anyone reviews anything.
     */
    private static function notifySaraHierarchy(array $actor, string $title, string $link = ''): void
    {
        try {
            $db = Database::connection();
            // Super admins
            $admins = $db->query("SELECT id FROM superadmins WHERE is_active = 1")->fetchAll(\PDO::FETCH_COLUMN);
            foreach ($admins as $adminId) {
                if ($actor['is_super_admin'] && (int)$adminId === $actor['user_id']) continue;
                \App\Models\Notification::push('superadmin', (int)$adminId, $title, '', $link);
            }
            // sara.lasher manager account
            $stmt = $db->prepare("SELECT id FROM managers WHERE username = 'sara.lasher' LIMIT 1");
            $stmt->execute();
            $row = $stmt->fetch();
            if ($row && (!($actor['user_type'] === 'manager') || (int)$row['id'] !== $actor['user_id'])) {
                \App\Models\Notification::push('manager', (int)$row['id'], $title, '', $link);
            }
        } catch (\Throwable $e) {}
    }

    /**
     * Push a notification to whoever the submitter directly reports to
     * (their direct supervisor) — so Cesar gets notified when Karan/Harpreet
     * submit a KPI, and Sara gets notified when Cesar submits.
     */
    private static function notifyDirectSupervisor(string $submitterType, int $submitterId, string $title, string $link = ''): void
    {
        try {
            $reportsTo = \App\Models\UserManager::currentReportsTo($submitterType, $submitterId);
            if (!$reportsTo) return;
            \App\Models\Notification::push($reportsTo['type'], $reportsTo['id'], $title, '', $link);
        } catch (\Throwable $e) {}
    }
}
