<?php
/**
 * DocExpiries — unified expiry tracking across truck_docs + driver_docs.
 *
 * Reads from the two existing doc tables (populated by TruckDocsController
 * and DriverDocsController upload actions) and buckets them by days-remaining
 * for the dashboard. The sweep runs on every 5-min cron tick, evaluates each
 * doc against the 3 severity brackets, and emits alerts via the shared
 * Alerts + Outbox pipeline built in Iter 3.2.
 *
 * The dashboard is a UNION over the two source tables — no separate expiry
 * table, no denormalisation. If a doc gets re-uploaded (renewal), the alert
 * cooldown lifts automatically because the expires_on jumps forward.
 */
class DocExpiries extends Model
{
    /**
     * Unified list of open documents with computed days-remaining.
     * $onlyExpiring: when true, filters out docs beyond 30 days.
     */
    public function listAll(bool $onlyExpiring = false, ?string $asOf = null): array
    {
        $asOf = $asOf ?: date('Y-m-d');
        $limitClause = $onlyExpiring ? "HAVING days_remaining <= 30" : "";

        $sql = "
            SELECT
                'truck'                                       AS source,
                td.c0d                                        AS doc_id,
                td.expires_on,
                td.doc_no,
                td.issuer,
                td.file_url,
                td.cost_amount,
                td.cost_currency,
                dt.slug                                       AS doc_slug,
                dt.display_name                               AS doc_type_name,
                dt.warn_days,
                td.truck_id                                   AS subject_id,
                t.reg_no                                      AS subject_name,
                TRIM(CONCAT(t.make, ' ', t.model))            AS subject_detail,
                DATEDIFF(td.expires_on, :asof)                AS days_remaining
              FROM truck_docs td
              JOIN truck_doc_types dt ON dt.c0d = td.doc_type_id
              JOIN trucks t          ON t.c0d = td.truck_id
             WHERE td.deleted = 0 AND td.expires_on IS NOT NULL
               AND dt.has_expiry = 1

            UNION ALL

            SELECT
                'driver'                                      AS source,
                dd.c0d                                        AS doc_id,
                dd.expires_on,
                dd.doc_no,
                dd.issuer,
                dd.file_url,
                NULL                                          AS cost_amount,
                NULL                                          AS cost_currency,
                dt.slug                                       AS doc_slug,
                dt.display_name                               AS doc_type_name,
                dt.warn_days,
                dd.driver_id                                  AS subject_id,
                d.full_name                                   AS subject_name,
                d.employee_no                                 AS subject_detail,
                DATEDIFF(dd.expires_on, :asof)                AS days_remaining
              FROM driver_docs dd
              JOIN driver_doc_types dt ON dt.c0d = dd.doc_type_id
              JOIN drivers d          ON d.c0d = dd.driver_id
             WHERE dd.deleted = 0 AND dd.expires_on IS NOT NULL
               AND dt.has_expiry = 1

          " . ($onlyExpiring ? " HAVING days_remaining <= 30 " : "") . "
          ORDER BY days_remaining ASC
        ";

        return (new DB())->mySelect($sql, [':asof' => $asOf]) ?: [];
    }

    /**
     * Bucketed counts for the dashboard KPI strip.
     */
    public function bucketCounts(?string $asOf = null): array
    {
        $rows = $this->listAll(false, $asOf);
        $b = ['expired' => 0, '0_7' => 0, '8_14' => 0, '15_30' => 0, '30_plus' => 0];
        foreach ($rows as $r) {
            $d = (int)$r['days_remaining'];
            if     ($d <  0)  $b['expired']++;
            elseif ($d <= 7)  $b['0_7']++;
            elseif ($d <= 14) $b['8_14']++;
            elseif ($d <= 30) $b['15_30']++;
            else              $b['30_plus']++;
        }
        return $b;
    }

    /**
     * Cron sweep. Called by CronController::tickAction every 5 min.
     * Returns [emitted, skipped] counts.
     */
    public function sweep(): array
    {
        $emitted = 0; $skipped = 0;
        $rules = new AlertRules();
        $ruleSoon    = $rules->activeByKind('doc_expiring_soon')[0]   ?? null;
        $ruleUrgent  = $rules->activeByKind('doc_expiring_urgent')[0] ?? null;
        $ruleExpired = $rules->activeByKind('doc_expired')[0]         ?? null;
        $alerts = new Alerts();

        foreach ($this->listAll(true) as $doc) {
            $days = (int)$doc['days_remaining'];

            // Bracket selection
            $rule = null; $stateWord = '';
            if     ($days <  0)                       { $rule = $ruleExpired; $stateWord = 'expired ' . abs($days) . ' days ago'; }
            elseif ($days >= 1  && $days <= 14)       { $rule = $ruleUrgent;  $stateWord = 'expires in ' . $days . ' days'; }
            elseif ($days >= 15 && $days <= 30)       { $rule = $ruleSoon;    $stateWord = 'expires in ' . $days . ' days'; }
            else                                       { continue; }

            if (!$rule) { $skipped++; continue; }

            $subject = $doc['source'] === 'truck' ? "Truck {$doc['subject_name']}" : "Driver {$doc['subject_name']}";
            $title   = "{$doc['doc_type_name']} · {$doc['subject_name']} · {$stateWord}";
            $body    = "{$doc['doc_type_name']} for {$subject}"
                     . ($doc['doc_no'] ? " (#{$doc['doc_no']})" : '')
                     . " {$stateWord}. Expiry date: {$doc['expires_on']}."
                     . ($doc['issuer'] ? " Issued by {$doc['issuer']}." : '');

            $ctx = [
                'ts'      => date('Y-m-d H:i:s'),
                'title'   => $title,
                'message' => $body,
                'payload' => [
                    'source'         => $doc['source'],
                    'doc_id'         => (int)$doc['doc_id'],
                    'doc_type'       => $doc['doc_slug'],
                    'expires_on'     => $doc['expires_on'],
                    'days_remaining' => $days,
                    'subject_id'     => (int)$doc['subject_id'],
                    'subject_name'   => $doc['subject_name'],
                ],
            ];
            if ($doc['source'] === 'truck')  $ctx['truck_id']  = (int)$doc['subject_id'];
            if ($doc['source'] === 'driver') $ctx['driver_id'] = (int)$doc['subject_id'];

            $alertId = $alerts->emit($rule, $ctx);
            if ($alertId) {
                Alerts::queueNotifications($rule, $alertId, $title, $body);
                $emitted++;
            } else {
                $skipped++;   // cooldown suppressed
            }
        }

        return ['emitted' => $emitted, 'skipped' => $skipped];
    }
}
