<?php

declare(strict_types=1);

namespace App\Repositories;

use App\Core\Database;
use Throwable;

/**
 * Phase 10.7.2 - Executive Leadership Typography & Readability Upgrade.
 *
 * Read-only, decision-focused leadership meeting brief. Unlike the main dashboard,
 * this repository deliberately avoids repeating the full KPI wall. It answers six
 * meeting questions: what changed, what needs a decision, what is overdue, who owns
 * what, what was decided, and what happens next.
 */
final class LeadershipHuddleRepository
{
    /** @var string */
    private $hospitalCode;

    public function __construct(string $hospitalCode)
    {
        $this->hospitalCode = strtolower(trim($hospitalCode)) ?: 'njch';
    }

    /** @return array<string,mixed> */
    public function report(): array
    {
        $center = (new ExecutiveActionRepository($this->hospitalCode))->center();
        $operational = is_array($center['operational'] ?? null) ? $center['operational'] : [];
        $operationalDate = $this->dateOnly((string)($operational['operational_date'] ?? date('Y-m-d')));
        $previousDate = date('Y-m-d', strtotime($operationalDate . ' -1 day'));
        $previous = $this->summarySnapshotForDate($previousDate);

        return $this->build($center, $previous);
    }

    /**
     * Public pure builder used by QA/runtime fixture tests as well as production.
     *
     * @param array<string,mixed> $center
     * @param array<string,mixed> $previousSnapshot
     * @return array<string,mixed>
     */
    public function build(array $center, array $previousSnapshot = []): array
    {
        $operational = is_array($center['operational'] ?? null) ? $center['operational'] : [];
        $operationalDate = $this->dateOnly((string)($operational['operational_date'] ?? date('Y-m-d')));
        $actions = is_array($center['actions'] ?? null) ? $center['actions'] : [];
        $metrics = is_array($center['metrics'] ?? null) ? $center['metrics'] : [];
        $system = is_array($center['system'] ?? null) ? $center['system'] : [];
        $reviewWorkflow = is_array($center['review_workflow'] ?? null) ? $center['review_workflow'] : [];
        $reviewManagement = is_array($reviewWorkflow['management'] ?? null) ? $reviewWorkflow['management'] : [];
        $reviewCounts = is_array($reviewWorkflow['counts'] ?? null) ? $reviewWorkflow['counts'] : [];
        $governanceRoot = is_array($center['governance'] ?? null) ? $center['governance'] : [];
        $governanceSummary = is_array($governanceRoot['summary'] ?? null) ? $governanceRoot['summary'] : [];

        $attention = $this->attentionItems($actions);
        $accountability = $this->accountabilityRows($actions);
        $notes = $this->managementNotes($actions);
        $comparison = $this->comparison($metrics, $previousSnapshot);

        // Phase 10.7.2: meeting-specific outputs with executive readability presentation. These intentionally avoid
        // duplicating the Executive Command Center KPI wall.
        $meaningfulChanges = $this->meaningfulChanges($comparison);
        $decisionAgenda = $this->decisionAgenda($actions);
        $openCommitments = $this->openCommitments($actions, $operationalDate);
        $documentedDecisions = $this->documentedDecisions($actions, $operationalDate);
        $ownerAccountability = is_array($governanceSummary['owner_workload'] ?? null)
            ? array_values($governanceSummary['owner_workload'])
            : [];
        $meetingStatus = $this->meetingStatus($decisionAgenda, $actions, $governanceSummary, $operationalDate);

        $topSeverity = 'stable';
        foreach ($attention as $item) {
            $severity = strtolower((string)($item['severity'] ?? 'routine'));
            if ($severity === 'critical') { $topSeverity = 'critical'; break; }
            if ($severity === 'watch' && $topSeverity !== 'critical') { $topSeverity = 'watch'; }
        }

        $freshness = !empty($center['summary_available'])
            ? (!empty($center['is_stale']) ? 'stale' : 'fresh')
            : 'unavailable';

        $reviewTotal = (int)($reviewManagement['total_actions'] ?? count($actions));
        $reviewed = (int)($reviewCounts['reviewed'] ?? $reviewManagement['reviewed'] ?? 0);
        $completion = $reviewTotal > 0
            ? (int)round(($reviewed / $reviewTotal) * 100)
            : 0;

        return [
            'phase' => '10.7.2',
            'title' => 'Leadership Huddle',
            'purpose' => 'decision_agenda_and_commitments',
            'purpose_questions' => [
                'what_changed',
                'what_needs_decision',
                'what_is_overdue',
                'who_owns_what',
                'what_was_decided',
                'what_happens_next',
            ],
            'hospital_code' => strtoupper($this->hospitalCode),
            'operational_date' => $operationalDate,
            'operational' => $operational,
            'last_updated' => (string)($center['last_updated'] ?? ''),
            'freshness' => $freshness,
            'summary_available' => !empty($center['summary_available']),
            'executive_status' => [
                'status' => (string)($system['status'] ?? 'STABLE'),
                'tone' => (string)($system['tone'] ?? $topSeverity),
                'text' => (string)($system['text'] ?? 'No blocking executive actions.'),
            ],
            // Kept for backwards compatibility with the Phase 10.7 QA evidence.
            // The redesigned view does not render the eight-card KPI wall.
            'headline_kpis' => $this->headlineKpis($metrics),
            'top_attention' => $attention,
            'accountability_rows' => $accountability,
            'management_notes' => $notes,
            'day_comparison' => $comparison,
            'meaningful_changes' => $meaningfulChanges,
            'decision_agenda' => $decisionAgenda,
            'open_commitments' => $openCommitments,
            'documented_decisions' => $documentedDecisions,
            'owner_accountability' => array_slice($ownerAccountability, 0, 6),
            'meeting_status' => $meetingStatus,
            'review' => [
                'total' => $reviewTotal,
                'open' => (int)($reviewCounts['open'] ?? $reviewManagement['open'] ?? 0),
                'reviewed' => $reviewed,
                'deferred' => (int)($reviewCounts['deferred'] ?? $reviewManagement['deferred'] ?? 0),
                'pending_attention' => (int)($reviewManagement['pending_attention'] ?? 0),
                'completion_percent' => (int)($reviewManagement['completion_percent'] ?? $completion),
                'storage' => (string)($reviewWorkflow['storage'] ?? ''),
            ],
            'governance' => [
                'active' => (int)($governanceSummary['active'] ?? count($actions)),
                'resolved' => (int)($governanceSummary['resolved'] ?? 0),
                'assigned' => (int)($governanceSummary['assigned'] ?? 0),
                'unassigned' => (int)($governanceSummary['unassigned'] ?? 0),
                'overdue' => (int)($governanceSummary['overdue'] ?? 0),
                'due_soon' => (int)($governanceSummary['due_soon'] ?? 0),
                'follow_up_due' => (int)($governanceSummary['follow_up_due'] ?? 0),
                'escalated' => (int)($governanceSummary['escalated'] ?? 0),
                'storage' => (string)($governanceRoot['storage'] ?? ''),
            ],
            'data_sources' => [
                'summary_layer' => !empty($center['summary_available']),
                'review_storage' => (string)($reviewWorkflow['storage'] ?? ''),
                'governance_storage' => (string)($governanceRoot['storage'] ?? ''),
                'previous_day_available' => !empty($previousSnapshot),
            ],
            'center' => $center,
        ];
    }

    /** @param array<string,mixed> $metrics @return array<int,array<string,mixed>> */
    private function headlineKpis(array $metrics): array
    {
        return [
            ['key' => 'today_total', 'label' => 'Revenue Today', 'label_ar' => 'إيراد اليوم', 'value' => $metrics['today_total'] ?? null, 'format' => 'money', 'tone' => 'purple'],
            ['key' => 'month_total', 'label' => 'Revenue MTD', 'label_ar' => 'إيراد الشهر', 'value' => $metrics['month_total'] ?? null, 'format' => 'money', 'tone' => 'blue'],
            ['key' => 'occupancy_rate', 'label' => 'Occupancy', 'label_ar' => 'الإشغال', 'value' => $metrics['occupancy_rate'] ?? null, 'format' => 'percent', 'tone' => 'teal'],
            ['key' => 'available_beds', 'label' => 'Available Beds', 'label_ar' => 'الأسرة المتاحة', 'value' => $metrics['available_beds'] ?? null, 'format' => 'number', 'tone' => 'green'],
            ['key' => 'admissions', 'label' => 'Admissions', 'label_ar' => 'الدخول', 'value' => $metrics['admissions'] ?? null, 'format' => 'number', 'tone' => 'purple'],
            ['key' => 'er', 'label' => 'ER Visits', 'label_ar' => 'زيارات الطوارئ', 'value' => $metrics['er'] ?? null, 'format' => 'number', 'tone' => 'red'],
            ['key' => 'opd', 'label' => 'OPD Visits', 'label_ar' => 'زيارات العيادات', 'value' => $metrics['opd'] ?? null, 'format' => 'number', 'tone' => 'teal'],
            ['key' => 'discharges', 'label' => 'Discharges', 'label_ar' => 'الخروج', 'value' => $metrics['discharges'] ?? null, 'format' => 'number', 'tone' => 'green'],
        ];
    }

    /**
     * Return only material changes that belong in a leadership meeting.
     * Routine KPI values remain on the main dashboard and are intentionally omitted here.
     *
     * @param array<string,mixed> $comparison
     * @return array<int,array<string,mixed>>
     */
    private function meaningfulChanges(array $comparison): array
    {
        if (empty($comparison['available'])) { return []; }
        $rows = is_array($comparison['rows'] ?? null) ? $comparison['rows'] : [];
        $thresholds = [
            'today_total' => ['absolute' => 50000.0, 'percent' => 5.0],
            'admissions' => ['absolute' => 3.0, 'percent' => 15.0],
            'er' => ['absolute' => 5.0, 'percent' => 15.0],
            'occupancy_rate' => ['absolute' => 1.0, 'percent' => 2.0],
            'available_beds' => ['absolute' => 3.0, 'percent' => 2.0],
        ];
        $changes = [];
        foreach ($rows as $row) {
            if (!is_array($row)) { continue; }
            $key = (string)($row['key'] ?? '');
            $delta = $row['delta'] ?? null;
            if ($delta === null || !isset($thresholds[$key])) { continue; }
            $absoluteDelta = abs((float)$delta);
            $percentDelta = abs((float)($row['delta_percent'] ?? 0));
            $absoluteThreshold = (float)$thresholds[$key]['absolute'];
            $percentThreshold = (float)$thresholds[$key]['percent'];
            $significant = $absoluteDelta >= $absoluteThreshold || $percentDelta >= $percentThreshold;
            if (!$significant) { continue; }

            $impact = max(
                $absoluteThreshold > 0 ? $absoluteDelta / $absoluteThreshold : 0,
                $percentThreshold > 0 ? $percentDelta / $percentThreshold : 0
            );
            $direction = (string)($row['direction'] ?? 'flat');
            $tone = 'info';
            if ($key === 'today_total') { $tone = $direction === 'down' ? 'danger' : ($direction === 'up' ? 'positive' : 'info'); }
            elseif ($key === 'available_beds' && $direction === 'down') { $tone = 'watch'; }
            elseif ($key === 'occupancy_rate' && $direction === 'up') { $tone = 'focus'; }

            $row['impact_score'] = $impact;
            $row['tone'] = $tone;
            $row['material'] = true;
            $changes[] = $row;
        }

        usort($changes, static function (array $a, array $b): int {
            return ((float)($b['impact_score'] ?? 0)) <=> ((float)($a['impact_score'] ?? 0));
        });
        return array_slice($changes, 0, 4);
    }

    /** @param array<int,array<string,mixed>> $actions @return array<int,array<string,mixed>> */
    private function decisionAgenda(array $actions): array
    {
        $rows = [];
        foreach ($actions as $action) {
            if (!is_array($action)) { continue; }
            $review = is_array($action['review'] ?? null) ? $action['review'] : [];
            $gov = is_array($action['governance'] ?? null) ? $action['governance'] : [];
            if (strtolower((string)($gov['resolution_status'] ?? 'active')) === 'resolved') { continue; }

            $severity = strtolower((string)($action['severity'] ?? 'routine'));
            $reviewStatus = strtolower((string)($review['status'] ?? $action['review_status'] ?? 'open'));
            $accountability = strtolower((string)($gov['accountability_status'] ?? $action['accountability_status'] ?? 'unassigned'));
            $escalation = strtolower((string)($gov['escalation_level'] ?? 'none'));

            $needsDecision = in_array($severity, ['critical', 'watch'], true)
                || in_array($accountability, ['overdue', 'follow_up_due', 'due_soon', 'escalated', 'unassigned'], true)
                || in_array($reviewStatus, ['open', 'deferred'], true);
            if (!$needsDecision) { continue; }

            $severityScore = ['critical' => 6000, 'watch' => 5000, 'opportunity' => 2600, 'routine' => 1800][$severity] ?? 1800;
            $accountabilityScore = ['overdue' => 4500, 'follow_up_due' => 3800, 'due_soon' => 3300, 'escalated' => 3000, 'unassigned' => 1800, 'assigned' => 600, 'on_track' => 200][$accountability] ?? 200;
            $reviewScore = ['deferred' => 1200, 'open' => 900, 'reviewed' => 0][$reviewStatus] ?? 500;
            $escalationScore = ['critical' => 2000, 'high' => 1400, 'watch' => 700, 'none' => 0][$escalation] ?? 0;

            $decisionType = 'Leadership decision';
            if ($accountability === 'overdue') { $decisionType = 'Recovery decision'; }
            elseif ($accountability === 'follow_up_due') { $decisionType = 'Follow-up decision'; }
            elseif ($accountability === 'due_soon') { $decisionType = 'Commitment confirmation'; }
            elseif ($accountability === 'unassigned') { $decisionType = 'Assign owner'; }
            elseif ($reviewStatus === 'deferred') { $decisionType = 'Resolve deferral'; }
            elseif ($severity === 'opportunity') { $decisionType = 'Opportunity review'; }

            $rows[] = [
                'action_key' => (string)($action['rule_id'] ?? ''),
                'title' => (string)($action['title'] ?? $action['rule_id'] ?? 'Action'),
                'title_ar' => (string)($action['title_ar'] ?? ''),
                'why' => (string)($action['why'] ?? $action['text'] ?? ''),
                'why_ar' => (string)($action['why_ar'] ?? $action['text_ar'] ?? ''),
                'decision_required' => (string)($action['recommendation'] ?? 'Agree the owner, due date and next step.'),
                'decision_required_ar' => (string)($action['recommendation_ar'] ?? 'اتفق على المسؤول وتاريخ الاستحقاق والخطوة التالية.'),
                'decision_type' => $decisionType,
                'severity' => $severity,
                'review_status' => $reviewStatus,
                'accountability_status' => $accountability,
                'owner_name' => trim((string)($gov['owner_name'] ?? '')),
                'assigned_to' => trim((string)($gov['assigned_to'] ?? '')),
                'due_date' => (string)($gov['due_date'] ?? ''),
                'follow_up_date' => (string)($gov['follow_up_date'] ?? ''),
                'escalation_level' => $escalation,
                '_score' => $severityScore + $accountabilityScore + $reviewScore + $escalationScore,
            ];
        }

        usort($rows, static function (array $a, array $b): int {
            $score = ((int)($b['_score'] ?? 0)) <=> ((int)($a['_score'] ?? 0));
            return $score !== 0 ? $score : strcmp((string)($a['title'] ?? ''), (string)($b['title'] ?? ''));
        });
        $rows = array_slice($rows, 0, 4);
        foreach ($rows as &$row) { unset($row['_score']); }
        unset($row);
        return $rows;
    }

    /** @param array<int,array<string,mixed>> $actions @return array<string,mixed> */
    private function openCommitments(array $actions, string $operationalDate): array
    {
        $rows = [];
        foreach ($actions as $action) {
            if (!is_array($action)) { continue; }
            $gov = is_array($action['governance'] ?? null) ? $action['governance'] : [];
            if (strtolower((string)($gov['resolution_status'] ?? 'active')) === 'resolved') { continue; }
            $accountability = strtolower((string)($gov['accountability_status'] ?? $action['accountability_status'] ?? 'unassigned'));
            $dueDate = (string)($gov['due_date'] ?? '');
            $dueToday = $dueDate !== '' && $this->dateOnly($dueDate) === $this->dateOnly($operationalDate);
            $severity = strtolower((string)($action['severity'] ?? 'routine'));
            $rank = ['overdue' => 1000, 'follow_up_due' => 900, 'due_soon' => 800, 'escalated' => 750, 'unassigned' => 650, 'assigned' => 350, 'on_track' => 200][$accountability] ?? 200;
            if ($dueToday) { $rank += 850; }
            $rank += ['critical' => 400, 'watch' => 300, 'routine' => 200, 'opportunity' => 100][$severity] ?? 100;

            $rows[] = [
                'action_key' => (string)($action['rule_id'] ?? ''),
                'title' => (string)($action['title'] ?? $action['rule_id'] ?? 'Action'),
                'title_ar' => (string)($action['title_ar'] ?? ''),
                'severity' => $severity,
                'owner_name' => trim((string)($gov['owner_name'] ?? '')),
                'assigned_to' => trim((string)($gov['assigned_to'] ?? '')),
                'due_date' => $dueDate,
                'follow_up_date' => (string)($gov['follow_up_date'] ?? ''),
                'accountability_status' => $accountability,
                'overdue_days' => (int)($gov['overdue_days'] ?? 0),
                'due_today' => $dueToday,
                '_score' => $rank,
            ];
        }
        usort($rows, static function (array $a, array $b): int { return ((int)$b['_score']) <=> ((int)$a['_score']); });
        $total = count($rows);
        $visible = array_slice($rows, 0, 5);
        foreach ($visible as &$row) { unset($row['_score']); }
        unset($row);
        return ['rows' => $visible, 'total' => $total, 'remaining' => max(0, $total - count($visible))];
    }

    /** @param array<int,array<string,mixed>> $actions @return array<int,array<string,mixed>> */
    private function documentedDecisions(array $actions, string $operationalDate): array
    {
        $rows = [];
        foreach ($actions as $action) {
            if (!is_array($action)) { continue; }
            $review = is_array($action['review'] ?? null) ? $action['review'] : [];
            $gov = is_array($action['governance'] ?? null) ? $action['governance'] : [];
            $title = (string)($action['title'] ?? $action['rule_id'] ?? 'Action');
            $titleAr = (string)($action['title_ar'] ?? '');

            $reviewActor = trim((string)($review['updated_by'] ?? ''));
            $reviewNote = trim((string)($review['note'] ?? ''));
            $reviewAt = (string)($review['updated_at'] ?? '');
            $reviewStatus = strtolower((string)($review['status'] ?? 'open'));
            if (in_array($reviewStatus, ['reviewed', 'deferred'], true)
                && $reviewNote !== ''
                && !$this->isQaActor($reviewActor)
                && !$this->isPlaceholderNote($reviewNote)
                && $this->isSameOperationalDate($reviewAt, $operationalDate)) {
                $rows[] = [
                    'type' => $reviewStatus === 'deferred' ? 'deferred_decision' : 'review_decision',
                    'action_key' => (string)($action['rule_id'] ?? ''),
                    'title' => $title,
                    'title_ar' => $titleAr,
                    'decision' => $reviewNote,
                    'by' => $reviewActor,
                    'at' => $reviewAt,
                ];
            }

            $resolutionNote = trim((string)($gov['resolution_note'] ?? ''));
            $resolvedBy = trim((string)($gov['resolved_by'] ?? $gov['updated_by'] ?? ''));
            $resolvedAt = (string)($gov['resolved_at'] ?? $gov['updated_at'] ?? '');
            if (strtolower((string)($gov['resolution_status'] ?? 'active')) === 'resolved'
                && $resolutionNote !== ''
                && !$this->isQaActor($resolvedBy)
                && !$this->isPlaceholderNote($resolutionNote)
                && $this->isSameOperationalDate($resolvedAt, $operationalDate)) {
                $rows[] = [
                    'type' => 'resolution',
                    'action_key' => (string)($action['rule_id'] ?? ''),
                    'title' => $title,
                    'title_ar' => $titleAr,
                    'decision' => $resolutionNote,
                    'by' => $resolvedBy,
                    'at' => $resolvedAt,
                ];
            }
        }
        usort($rows, static function (array $a, array $b): int { return strcmp((string)($b['at'] ?? ''), (string)($a['at'] ?? '')); });
        return array_slice($rows, 0, 6);
    }

    /**
     * @param array<int,array<string,mixed>> $agenda
     * @param array<int,array<string,mixed>> $actions
     * @param array<string,mixed> $governanceSummary
     * @return array<string,mixed>
     */
    private function meetingStatus(array $agenda, array $actions, array $governanceSummary, string $operationalDate): array
    {
        $dueToday = 0;
        $critical = 0;
        foreach ($actions as $action) {
            if (!is_array($action)) { continue; }
            if (strtolower((string)($action['severity'] ?? 'routine')) === 'critical') { $critical++; }
            $gov = is_array($action['governance'] ?? null) ? $action['governance'] : [];
            if (strtolower((string)($gov['resolution_status'] ?? 'active')) === 'resolved') { continue; }
            $due = trim((string)($gov['due_date'] ?? ''));
            if ($due !== '' && $this->dateOnly($due) === $this->dateOnly($operationalDate)) { $dueToday++; }
        }
        $overdue = (int)($governanceSummary['overdue'] ?? 0);
        $followUpDue = (int)($governanceSummary['follow_up_due'] ?? 0);
        $unassigned = (int)($governanceSummary['unassigned'] ?? 0);
        $escalated = (int)($governanceSummary['escalated'] ?? 0);

        $state = 'READY';
        $tone = 'good';
        if ($critical > 0 || $overdue > 0) { $state = 'ACTION REQUIRED'; $tone = 'critical'; }
        elseif (count($agenda) > 0 || $followUpDue > 0 || $unassigned > 0 || $escalated > 0) { $state = 'FOCUS'; $tone = 'watch'; }

        return [
            'state' => $state,
            'tone' => $tone,
            'decision_count' => count($agenda),
            'overdue' => $overdue,
            'follow_up_due' => $followUpDue,
            'unassigned' => $unassigned,
            'due_today' => $dueToday,
            'escalated' => $escalated,
            'critical' => $critical,
        ];
    }

    private function isPlaceholderNote(string $note): bool
    {
        $value = strtolower(trim($note));
        return in_array($value, ['test', 'testing', 'test note', 'demo', 'ok', 'n/a', 'na', '-'], true);
    }

    private function isSameOperationalDate(string $dateTime, string $operationalDate): bool
    {
        if (trim($dateTime) === '') { return false; }
        $ts = strtotime($dateTime);
        return $ts !== false && date('Y-m-d', $ts) === $this->dateOnly($operationalDate);
    }

    /** @param array<int,array<string,mixed>> $actions @return array<int,array<string,mixed>> */
    private function attentionItems(array $actions): array
    {
        $rows = [];
        foreach ($actions as $action) {
            if (!is_array($action)) { continue; }
            $review = is_array($action['review'] ?? null) ? $action['review'] : [];
            $gov = is_array($action['governance'] ?? null) ? $action['governance'] : [];
            $severity = strtolower((string)($action['severity'] ?? 'routine'));
            $reviewStatus = strtolower((string)($review['status'] ?? $action['review_status'] ?? 'open'));
            $accountability = strtolower((string)($gov['accountability_status'] ?? $action['accountability_status'] ?? 'unassigned'));
            $resolution = strtolower((string)($gov['resolution_status'] ?? 'active'));
            if ($resolution === 'resolved') { continue; }

            $severityScore = ['critical' => 5000, 'watch' => 4000, 'opportunity' => 2500, 'routine' => 1500][$severity] ?? 1500;
            $accountabilityScore = ['overdue' => 3000, 'follow_up_due' => 2500, 'due_soon' => 2000, 'escalated' => 1800, 'unassigned' => 1200, 'assigned' => 500, 'on_track' => 200][$accountability] ?? 200;
            $reviewScore = ['deferred' => 700, 'open' => 500, 'reviewed' => 0][$reviewStatus] ?? 500;

            $rows[] = [
                'action_key' => (string)($action['rule_id'] ?? ''),
                'title' => (string)($action['title'] ?? $action['rule_id'] ?? 'Action'),
                'title_ar' => (string)($action['title_ar'] ?? ''),
                'text' => (string)($action['text'] ?? ''),
                'text_ar' => (string)($action['text_ar'] ?? ''),
                'severity' => $severity,
                'review_status' => $reviewStatus,
                'accountability_status' => $accountability,
                'owner_name' => (string)($gov['owner_name'] ?? ''),
                'assigned_to' => (string)($gov['assigned_to'] ?? ''),
                'due_date' => (string)($gov['due_date'] ?? ''),
                'follow_up_date' => (string)($gov['follow_up_date'] ?? ''),
                'escalation_level' => (string)($gov['escalation_level'] ?? 'none'),
                'score' => $severityScore + $accountabilityScore + $reviewScore,
            ];
        }

        usort($rows, static function (array $a, array $b): int {
            $score = ((int)($b['score'] ?? 0)) <=> ((int)($a['score'] ?? 0));
            if ($score !== 0) { return $score; }
            return strcmp((string)($a['title'] ?? ''), (string)($b['title'] ?? ''));
        });

        return array_slice($rows, 0, 5);
    }

    /** @param array<int,array<string,mixed>> $actions @return array<int,array<string,mixed>> */
    private function accountabilityRows(array $actions): array
    {
        $rows = [];
        foreach ($actions as $action) {
            if (!is_array($action)) { continue; }
            $gov = is_array($action['governance'] ?? null) ? $action['governance'] : [];
            $review = is_array($action['review'] ?? null) ? $action['review'] : [];
            $rows[] = [
                'action_key' => (string)($action['rule_id'] ?? ''),
                'title' => (string)($action['title'] ?? $action['rule_id'] ?? 'Action'),
                'title_ar' => (string)($action['title_ar'] ?? ''),
                'severity' => strtolower((string)($action['severity'] ?? 'routine')),
                'review_status' => strtolower((string)($review['status'] ?? $action['review_status'] ?? 'open')),
                'owner_name' => trim((string)($gov['owner_name'] ?? '')),
                'assigned_to' => trim((string)($gov['assigned_to'] ?? '')),
                'due_date' => (string)($gov['due_date'] ?? ''),
                'follow_up_date' => (string)($gov['follow_up_date'] ?? ''),
                'accountability_status' => strtolower((string)($gov['accountability_status'] ?? $action['accountability_status'] ?? 'unassigned')),
                'overdue_days' => (int)($gov['overdue_days'] ?? 0),
                'escalation_level' => strtolower((string)($gov['escalation_level'] ?? 'none')),
                'resolution_status' => strtolower((string)($gov['resolution_status'] ?? 'active')),
            ];
        }

        $rank = ['overdue' => 900, 'follow_up_due' => 800, 'due_soon' => 700, 'escalated' => 650, 'unassigned' => 600, 'assigned' => 300, 'on_track' => 200, 'resolved' => 0];
        $sev = ['critical' => 400, 'watch' => 300, 'routine' => 200, 'opportunity' => 100];
        usort($rows, static function (array $a, array $b) use ($rank, $sev): int {
            $aScore = ($rank[$a['accountability_status']] ?? 100) + ($sev[$a['severity']] ?? 100);
            $bScore = ($rank[$b['accountability_status']] ?? 100) + ($sev[$b['severity']] ?? 100);
            return $bScore <=> $aScore;
        });

        return $rows;
    }

    /** @param array<int,array<string,mixed>> $actions @return array<int,array<string,mixed>> */
    private function managementNotes(array $actions): array
    {
        $notes = [];
        foreach ($actions as $action) {
            if (!is_array($action)) { continue; }
            $review = is_array($action['review'] ?? null) ? $action['review'] : [];
            $gov = is_array($action['governance'] ?? null) ? $action['governance'] : [];
            $actor = trim((string)($review['updated_by'] ?? ''));
            $note = trim((string)($review['note'] ?? ''));
            if ($note !== '' && !$this->isQaActor($actor)) {
                $notes[] = [
                    'type' => 'review',
                    'action_key' => (string)($action['rule_id'] ?? ''),
                    'title' => (string)($action['title'] ?? $action['rule_id'] ?? 'Action'),
                    'title_ar' => (string)($action['title_ar'] ?? ''),
                    'note' => $note,
                    'by' => $actor,
                    'at' => (string)($review['updated_at'] ?? ''),
                ];
            }
            $resolutionNote = trim((string)($gov['resolution_note'] ?? ''));
            $resolvedBy = trim((string)($gov['resolved_by'] ?? $gov['updated_by'] ?? ''));
            if ($resolutionNote !== '' && !$this->isQaActor($resolvedBy)) {
                $notes[] = [
                    'type' => 'resolution',
                    'action_key' => (string)($action['rule_id'] ?? ''),
                    'title' => (string)($action['title'] ?? $action['rule_id'] ?? 'Action'),
                    'title_ar' => (string)($action['title_ar'] ?? ''),
                    'note' => $resolutionNote,
                    'by' => $resolvedBy,
                    'at' => (string)($gov['resolved_at'] ?? $gov['updated_at'] ?? ''),
                ];
            }
        }

        usort($notes, static function (array $a, array $b): int {
            return strcmp((string)($b['at'] ?? ''), (string)($a['at'] ?? ''));
        });

        return array_slice($notes, 0, 6);
    }

    /**
     * @param array<string,mixed> $current
     * @param array<string,mixed> $previous
     * @return array<string,mixed>
     */
    private function comparison(array $current, array $previous): array
    {
        $keys = [
            'today_total' => ['label' => 'Revenue Today', 'label_ar' => 'إيراد اليوم', 'format' => 'money'],
            'admissions' => ['label' => 'Admissions', 'label_ar' => 'الدخول', 'format' => 'number'],
            'er' => ['label' => 'ER Visits', 'label_ar' => 'زيارات الطوارئ', 'format' => 'number'],
            'occupancy_rate' => ['label' => 'Occupancy', 'label_ar' => 'الإشغال', 'format' => 'percent'],
            'available_beds' => ['label' => 'Available Beds', 'label_ar' => 'الأسرة المتاحة', 'format' => 'number'],
        ];
        $rows = [];
        foreach ($keys as $key => $meta) {
            $currentValue = isset($current[$key]) && $current[$key] !== '' ? (float)$current[$key] : null;
            $previousValue = isset($previous[$key]) && $previous[$key] !== '' ? (float)$previous[$key] : null;
            $delta = ($currentValue !== null && $previousValue !== null) ? $currentValue - $previousValue : null;
            $pct = ($delta !== null && $previousValue !== 0.0) ? ($delta / abs($previousValue)) * 100 : null;
            $rows[] = [
                'key' => $key,
                'label' => $meta['label'],
                'label_ar' => $meta['label_ar'],
                'format' => $meta['format'],
                'current' => $currentValue,
                'previous' => $previousValue,
                'delta' => $delta,
                'delta_percent' => $pct,
                'direction' => $delta === null || abs($delta) < 0.0001 ? 'flat' : ($delta > 0 ? 'up' : 'down'),
            ];
        }

        return [
            'available' => !empty($previous),
            'previous_operational_date' => (string)($previous['_summary_date'] ?? ''),
            'rows' => $rows,
        ];
    }

    /** @return array<string,mixed> */
    private function summarySnapshotForDate(string $date): array
    {
        try {
            $rows = Database::fetchAll($this->hospitalCode, "
                SELECT
                    kpi_code,
                    kpi_value,
                    kpi_text,
                    TO_CHAR(summary_date, 'YYYY-MM-DD') summary_date_text
                FROM dash_exec_summary_kpi
                WHERE hospital_code = :hospital_code
                  AND summary_date = TO_DATE(:summary_date, 'YYYY-MM-DD')
            ", [
                'hospital_code' => strtoupper($this->hospitalCode),
                'summary_date' => $this->dateOnly($date),
            ]);
            if (!$rows) { return []; }
            $snapshot = ['_summary_date' => $this->dateOnly($date)];
            foreach ($rows as $row) {
                if (!is_array($row)) { continue; }
                $key = strtolower(trim((string)($row['kpi_code'] ?? '')));
                if ($key === '') { continue; }
                $snapshot[$key] = $row['kpi_value'] ?? $row['kpi_text'] ?? null;
            }
            return $snapshot;
        } catch (Throwable $e) {
            $this->log('summarySnapshotForDate', $e);
            return [];
        }
    }

    private function isQaActor(string $actor): bool
    {
        $actor = strtolower(trim($actor));
        if ($actor === '') { return false; }
        if (strpos($actor, 'phase10_') === 0 && strpos($actor, '_qa') !== false) { return true; }
        if (strpos($actor, 'qa_') === 0 || substr($actor, -3) === '_qa') { return true; }
        $extra = trim((string)\env('EXEC_ACTION_REVIEW_QA_ACTORS', ''));
        if ($extra !== '') {
            foreach (explode(',', strtolower($extra)) as $pattern) {
                $pattern = trim($pattern);
                if ($pattern === '') { continue; }
                if (substr($pattern, -1) === '*' && strpos($actor, substr($pattern, 0, -1)) === 0) { return true; }
                if ($actor === $pattern) { return true; }
            }
        }
        return false;
    }

    private function dateOnly(string $value): string
    {
        $value = trim($value);
        if ($value === '') { return date('Y-m-d'); }
        $ts = strtotime($value);
        return $ts === false ? date('Y-m-d') : date('Y-m-d', $ts);
    }

    private function log(string $context, Throwable $e): void
    {
        $file = \storage_path('logs/app.log');
        $dir = dirname($file);
        if (!is_dir($dir)) { @mkdir($dir, 0775, true); }
        @file_put_contents($file, '[' . date('c') . '] LeadershipHuddleRepository ' . $context . ': ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
    }
}
