<?php
declare(strict_types=1);

require_once __DIR__ . '/notification.php';
require_once __DIR__ . '/mailer.php';

function alertDispatch(PDO $pdo, array $payload): array
{
    ensureAlertInfrastructure($pdo);

    $title = trim((string)($payload['title'] ?? 'Fleet Alert'));
    $message = trim((string)($payload['message'] ?? ''));
    if ($message === '') {
        $message = $title;
    }

    $channels = $payload['channels'] ?? ['in_app', 'email', 'sms'];
    $channels = array_values(array_unique(array_filter(array_map('strval', (array)$channels))));
    $users = alertResolveUsers($pdo, $payload);

    $result = ['users' => count($users), 'deliveries' => []];
    foreach ($users as $user) {
        foreach ($channels as $channel) {
            $result['deliveries'][] = alertDeliverToUser($pdo, $user, $channel, $payload, $title, $message);
        }
    }

    return $result;
}

function alertUsersByRoles(PDO $pdo, array $roles, string $title, string $message, array $options = []): array
{
    $options['roles'] = $roles;
    $options['title'] = $title;
    $options['message'] = $message;
    return alertDispatch($pdo, $options);
}

function alertUsersByIds(PDO $pdo, array $userIds, string $title, string $message, array $options = []): array
{
    $options['user_ids'] = $userIds;
    $options['title'] = $title;
    $options['message'] = $message;
    return alertDispatch($pdo, $options);
}

function alertResolveUsers(PDO $pdo, array $payload): array
{
    $byId = array_values(array_unique(array_filter(array_map('intval', (array)($payload['user_ids'] ?? [])))));
    $roles = array_values(array_unique(array_filter(array_map('strval', (array)($payload['roles'] ?? [])))));

    if (!$byId && !$roles) {
        return [];
    }

    $params = [];
    $where = [];

    if ($byId) {
        $where[] = 'u.id IN (' . implode(',', array_fill(0, count($byId), '?')) . ')';
        $params = array_merge($params, $byId);
    }

    $roleJoin = '';
    if ($roles) {
        $roleJoin = "
            LEFT JOIN roles primary_role ON primary_role.id = u.role_id
            LEFT JOIN user_roles ur ON ur.user_id = u.id
            LEFT JOIN roles assigned_role ON assigned_role.id = ur.role_id
        ";
        $where[] = '(primary_role.name IN (' . implode(',', array_fill(0, count($roles), '?')) . ') OR assigned_role.name IN (' . implode(',', array_fill(0, count($roles), '?')) . '))';
        $params = array_merge($params, $roles, $roles);
    }

    $sql = "
        SELECT DISTINCT
            u.id,
            u.name,
            u.email,
            " . (alertColumnExists($pdo, 'users', 'phone') ? 'u.phone' : 'NULL') . " AS phone,
            " . (alertColumnExists($pdo, 'users', 'email_alerts_enabled') ? 'u.email_alerts_enabled' : '1') . " AS email_alerts_enabled,
            " . (alertColumnExists($pdo, 'users', 'sms_alerts_enabled') ? 'u.sms_alerts_enabled' : '1') . " AS sms_alerts_enabled
        FROM users u
        {$roleJoin}
        WHERE COALESCE(u.is_active, 1) = 1
          AND (" . implode(' OR ', $where) . ")
        ORDER BY u.name ASC
    ";

    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

function alertDeliverToUser(PDO $pdo, array $user, string $channel, array $payload, string $title, string $message): array
{
    $type = (string)($payload['type'] ?? $payload['event'] ?? 'general');
    $url = $payload['url'] ?? null;
    $referenceId = isset($payload['reference_id']) ? (int)$payload['reference_id'] : null;
    $referenceType = $payload['reference_type'] ?? null;
    $recipient = '';
    $status = 'skipped';
    $providerResponse = null;

    try {
        if ($channel === 'in_app') {
            notify($pdo, (int)$user['id'], $title, $message, $type, $url, $referenceId);
            $recipient = (string)$user['id'];
            $status = 'sent';
        } elseif ($channel === 'email') {
            $recipient = (string)($user['email'] ?? '');
            if ((int)($user['email_alerts_enabled'] ?? 1) !== 1 || !filter_var($recipient, FILTER_VALIDATE_EMAIL)) {
                $status = 'skipped';
                $providerResponse = 'Missing email address or email alerts disabled.';
            } else {
                $body = alertBuildEmailBody($title, $message, $payload);
                $status = sendMail($recipient, $title, $body) ? 'sent' : 'failed';
            }
        } elseif ($channel === 'sms') {
            $recipient = alertNormalizePhone((string)($user['phone'] ?? ''));
            if ((int)($user['sms_alerts_enabled'] ?? 1) !== 1 || $recipient === '') {
                $status = 'skipped';
                $providerResponse = 'Missing phone number or SMS alerts disabled.';
            } else {
                $sms = sendSms($recipient, alertBuildSmsMessage($title, $message), $payload);
                $status = $sms['status'];
                $providerResponse = $sms['response'];
            }
        }
    } catch (Throwable $e) {
        $status = 'failed';
        $providerResponse = $e->getMessage();
    }

    alertLogDelivery($pdo, [
        'event_key' => (string)($payload['event'] ?? $type),
        'channel' => $channel,
        'user_id' => (int)$user['id'],
        'recipient' => $recipient,
        'subject' => $title,
        'message' => $message,
        'status' => $status,
        'provider_response' => $providerResponse,
        'payload_json' => json_encode($payload),
        'reference_type' => $referenceType,
        'reference_id' => $referenceId,
    ]);

    return ['user_id' => (int)$user['id'], 'channel' => $channel, 'status' => $status];
}

function sendSms(string $to, string $message, array $payload = []): array
{
    $config = require __DIR__ . '/../../config/alerts.php';
    if (empty($config['sms_enabled'])) {
        return ['status' => 'skipped', 'response' => 'SMS disabled. Set FLEET_ALERT_SMS_ENABLED=1 to enable delivery.'];
    }

    if (($config['sms_provider'] ?? 'log') === 'log' || empty($config['sms_webhook_url'])) {
        return ['status' => 'skipped', 'response' => 'No SMS webhook configured.'];
    }

    if (!function_exists('curl_init')) {
        return ['status' => 'failed', 'response' => 'PHP cURL extension is not available.'];
    }

    $body = json_encode([
        'to' => $to,
        'message' => $message,
        'sender' => $config['sms_sender'] ?? 'FleetOps',
        'event' => $payload['event'] ?? null,
    ]);

    $ch = curl_init((string)$config['sms_webhook_url']);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => $body,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 10,
        CURLOPT_HTTPHEADER => array_filter([
            'Content-Type: application/json',
            !empty($config['sms_api_key']) ? 'Authorization: Bearer ' . $config['sms_api_key'] : null,
        ]),
    ]);

    $response = curl_exec($ch);
    $error = curl_error($ch);
    $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($response === false || $code >= 400) {
        return ['status' => 'failed', 'response' => $error ?: (string)$response ?: 'HTTP ' . $code];
    }

    return alertParseSmsProviderResponse((string)$response, $code);
}

function alertParseSmsProviderResponse(string $response, int $httpCode): array
{
    $payload = json_decode($response, true);
    if (!is_array($payload)) {
        return ['status' => $httpCode >= 200 && $httpCode < 300 ? 'sent' : 'failed', 'response' => $response];
    }

    $statusText = strtolower((string)($payload['status'] ?? $payload['message_status'] ?? $payload['state'] ?? ''));
    $success = (bool)($payload['success'] ?? false);
    $failed = (bool)($payload['error'] ?? false);

    if ($success || in_array($statusText, ['sent', 'success', 'successful', 'queued', 'accepted', 'delivered'], true)) {
        return ['status' => 'sent', 'response' => $response];
    }

    if ($failed || in_array($statusText, ['failed', 'error', 'rejected', 'undelivered'], true)) {
        return ['status' => 'failed', 'response' => $response];
    }

    return ['status' => 'sent', 'response' => $response];
}

function alertBuildEmailBody(string $title, string $message, array $payload): string
{
    $safeTitle = htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
    $safeMessage = nl2br(htmlspecialchars($message, ENT_QUOTES, 'UTF-8'));
    $url = (string)($payload['url'] ?? '');
    $config = require __DIR__ . '/../../config/alerts.php';
    $absoluteUrl = $url !== '' && !preg_match('/^https?:\/\//', $url) && !empty($config['app_url'])
        ? rtrim((string)$config['app_url'], '/') . '/' . ltrim($url, '/')
        : $url;
    $link = $absoluteUrl !== ''
        ? '<p style="margin-top:18px;"><a href="' . htmlspecialchars($absoluteUrl, ENT_QUOTES, 'UTF-8') . '" style="background:#1d4ed8;color:#fff;padding:10px 14px;text-decoration:none;border-radius:6px;">Open in Fleet System</a></p>'
        : '';

    return '<div style="font-family:Arial,sans-serif;color:#102033;line-height:1.55;">'
        . '<h2 style="margin:0 0 10px;">' . $safeTitle . '</h2>'
        . '<p style="margin:0;">' . $safeMessage . '</p>'
        . $link
        . '<p style="margin-top:20px;color:#667085;font-size:12px;">This alert was sent by Fleet Operations.</p>'
        . '</div>';
}

function alertBuildSmsMessage(string $title, string $message): string
{
    $text = trim($title . ': ' . preg_replace('/\s+/', ' ', $message));
    return strlen($text) > 300 ? substr($text, 0, 297) . '...' : $text;
}

function alertNormalizePhone(string $phone): string
{
    $phone = trim($phone);
    if ($phone === '') {
        return '';
    }
    $normalized = preg_replace('/[^\d+]/', '', $phone);
    return $normalized ?: '';
}

function alertLogDelivery(PDO $pdo, array $row): void
{
    try {
        $stmt = $pdo->prepare("
            INSERT INTO alert_delivery_logs (
                event_key, channel, user_id, recipient, subject, message, status,
                provider_response, payload_json, attempts, next_attempt_at, delivered_at,
                reference_type, reference_id, created_at
            ) VALUES (
                :event_key, :channel, :user_id, :recipient, :subject, :message, :status,
                :provider_response, :payload_json, 1,
                CASE WHEN :status_retry = 'failed' THEN DATE_ADD(NOW(), INTERVAL 5 MINUTE) ELSE NULL END,
                CASE WHEN :status_delivered = 'sent' THEN NOW() ELSE NULL END,
                :reference_type, :reference_id, NOW()
            )
        ");
        $row['status_retry'] = $row['status'];
        $row['status_delivered'] = $row['status'];
        $stmt->execute($row);
    } catch (Throwable) {
        // Logging must never interrupt business workflows.
    }
}

function alertRetryDelivery(PDO $pdo, int $deliveryId): array
{
    $stmt = $pdo->prepare("SELECT * FROM alert_delivery_logs WHERE id = ? LIMIT 1");
    $stmt->execute([$deliveryId]);
    $delivery = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$delivery) {
        return ['status' => 'error', 'message' => 'Delivery log not found'];
    }

    if ((int)($delivery['user_id'] ?? 0) <= 0) {
        return ['status' => 'error', 'message' => 'Delivery has no user to retry'];
    }

    $stmt = $pdo->prepare("
        SELECT id, name, email, phone, email_alerts_enabled, sms_alerts_enabled
        FROM users
        WHERE id = ?
        LIMIT 1
    ");
    $stmt->execute([(int)$delivery['user_id']]);
    $user = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$user) {
        return ['status' => 'error', 'message' => 'User not found'];
    }

    $payload = json_decode((string)($delivery['payload_json'] ?? ''), true);
    if (!is_array($payload)) {
        $payload = ['event' => $delivery['event_key'], 'reference_type' => $delivery['reference_type'], 'reference_id' => $delivery['reference_id']];
    }

    $result = alertDeliverToUser(
        $pdo,
        $user,
        (string)$delivery['channel'],
        $payload,
        (string)$delivery['subject'],
        (string)$delivery['message']
    );

    $status = $result['status'] ?? 'failed';
    $stmt = $pdo->prepare("
        UPDATE alert_delivery_logs
        SET attempts = attempts + 1,
            status = ?,
            next_attempt_at = CASE WHEN ? = 'failed' THEN DATE_ADD(NOW(), INTERVAL LEAST(60, POW(2, attempts)) MINUTE) ELSE NULL END,
            delivered_at = CASE WHEN ? = 'sent' THEN NOW() ELSE delivered_at END
        WHERE id = ?
    ");
    $stmt->execute([$status, $status, $status, $deliveryId]);

    return ['status' => 'success', 'delivery_status' => $status];
}

function ensureAlertInfrastructure(PDO $pdo): void
{
    static $done = false;
    if ($done) {
        return;
    }

    // Schema is managed by database/migrations/2026_05_28_application_alerts.sql.
    $done = true;
}

function alertColumnExists(PDO $pdo, string $table, string $column): bool
{
    $stmt = $pdo->prepare("
        SELECT COUNT(*)
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_SCHEMA = DATABASE()
          AND TABLE_NAME = ?
          AND COLUMN_NAME = ?
    ");
    $stmt->execute([$table, $column]);
    return (int)$stmt->fetchColumn() > 0;
}

function alertAddColumnIfMissing(PDO $pdo, string $table, string $column, string $definition): void
{
    // Kept for backward compatibility with older includes. Runtime DDL is intentionally disabled.
}
