<?php
declare(strict_types=1);

namespace App\Modules\Notifications;

use PDO;

final class NotificationRepository
{
    public function __construct(private readonly PDO $pdo)
    {
    }

    /**
     * @param array<string, mixed> $data
     */
    public function create(array $data): int
    {
        $statement = $this->pdo->prepare(
            'INSERT INTO notifications (
                type, title, body, target_url, related_order_id, related_sms_message_id, created_at
            ) VALUES (
                :type, :title, :body, :target_url, :related_order_id, :related_sms_message_id, NOW()
            )'
        );
        $statement->execute([
            'type' => substr(trim((string) ($data['type'] ?? 'info')), 0, 64),
            'title' => substr(trim((string) ($data['title'] ?? 'Powiadomienie')), 0, 190),
            'body' => substr(trim((string) ($data['body'] ?? '')), 0, 500),
            'target_url' => $this->nullableString((string) ($data['target_url'] ?? '')),
            'related_order_id' => $this->nullableInt($data['related_order_id'] ?? null),
            'related_sms_message_id' => $this->nullableInt($data['related_sms_message_id'] ?? null),
        ]);

        return (int) $this->pdo->lastInsertId();
    }

    /**
     * @return array{items: array<int, array<string, mixed>>, total: int, page: int, per_page: int}
     */
    public function paginate(int $page = 1, int $perPage = 30): array
    {
        $safePage = max(1, $page);
        $safePerPage = max(1, min(100, $perPage));
        $offset = ($safePage - 1) * $safePerPage;

        $total = (int) $this->pdo->query('SELECT COUNT(*) FROM notifications')->fetchColumn();
        $statement = $this->pdo->prepare(
            'SELECT *
             FROM notifications
             ORDER BY created_at DESC, id DESC
             LIMIT :limit OFFSET :offset'
        );
        $statement->bindValue('limit', $safePerPage, PDO::PARAM_INT);
        $statement->bindValue('offset', $offset, PDO::PARAM_INT);
        $statement->execute();
        $items = $statement->fetchAll(PDO::FETCH_ASSOC);

        return [
            'items' => is_array($items) ? $items : [],
            'total' => $total,
            'page' => $safePage,
            'per_page' => $safePerPage,
        ];
    }

    public function unreadCount(): int
    {
        $statement = $this->pdo->prepare('SELECT COUNT(*) FROM notifications WHERE read_at IS NULL');
        $statement->execute();

        return (int) $statement->fetchColumn();
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function recentUnread(int $limit = 10): array
    {
        $safeLimit = max(1, min(50, $limit));
        $statement = $this->pdo->prepare(
            'SELECT *
             FROM notifications
             WHERE read_at IS NULL
             ORDER BY created_at DESC, id DESC
             LIMIT :limit'
        );
        $statement->bindValue('limit', $safeLimit, PDO::PARAM_INT);
        $statement->execute();
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);

        return is_array($rows) ? $rows : [];
    }

    public function markRead(int $id): void
    {
        if ($id <= 0) {
            return;
        }

        $statement = $this->pdo->prepare(
            'UPDATE notifications
             SET read_at = COALESCE(read_at, NOW())
             WHERE id = :id'
        );
        $statement->execute(['id' => $id]);
    }

    public function markAllRead(): void
    {
        $this->pdo->prepare('UPDATE notifications SET read_at = NOW() WHERE read_at IS NULL')->execute();
    }

    private function nullableString(string $value): ?string
    {
        $trimmed = trim($value);

        return $trimmed === '' ? null : $trimmed;
    }

    private function nullableInt(mixed $value): ?int
    {
        $intValue = (int) $value;

        return $intValue > 0 ? $intValue : null;
    }
}
