<?php
/**
 * BookiFlex Notification Extension
 *
 * @package    BookiFlex
 * @subpackage Notification
 * @author     BookiFlex
 * @copyright  Copyright (c) 2025 BookiFlex
 * @license    GPL-2.0-or-later
 *
 * @link       https://bookiflex.com
 */

declare(strict_types=1);

 namespace BookiFlex\Extensions\Notification\Repository;

use BookiFlex\Core\Model\Reservation\Reservation;
use BookiFlex\Extensions\Notification\Model\Notification;
use BookiFlex\Extensions\Notification\Model\NotificationStatus;
use Cycle\ORM\Select\Repository;

defined( 'ABSPATH' ) || exit;

class NotificationRepository extends Repository
{
    public function findByReservation(Reservation $reservation)
    {
        return $this->findAll(['reservation_id' => $reservation->getId()]);
    }

    public function findBySid(string $sid)
    {
        return $this->findOne(['sid' => $sid]);
    }

    /**
     * Find notifications that are due to be sent.
     *
     * @return Notification[]
     */
    public function findDue(int $maxAttempts = Notification::MAX_ATTEMPTS): array
    {
        return $this->select()
            ->where('status', NotificationStatus::PENDING)
            ->where('send_time', '<=', new \DateTimeImmutable('now', new \DateTimeZone('UTC')))
            ->where('attempt', '<', $maxAttempts)
            ->fetchAll();
    }

    /**
     * Find client notifications by reservation ID.
     *
     * @return Notification[]
     */
    public function findClientByReservationId(int $reservationId): array
    {
        return $this->findAll([
            'reservation_id' => $reservationId,
            'stakeholder'    => 'client',
        ]);
    }

    /**
     * Find all pending notifications for a given reservation.
     *
     * @return Notification[]
     */
    public function findPendingByReservation(Reservation $reservation): array
    {
        return $this->findAll([
            'reservation_id' => $reservation->getId(),
            'status'         => NotificationStatus::PENDING,
        ]);
    }

    /**
     * Find paginated notifications with optional filters.
     * Eagerly loads the reservation relation to avoid N+1 queries.
     *
     * @return Notification[]
     */
    public function findPaginated(
        ?int $reservationId,
        ?string $status,
        ?string $stakeholder,
        string $orderBy = 'send_time',
        string $order = 'DESC',
        int $limit = 20,
        int $offset = 0,
    ): array {
        $query = $this->select()
            ->load('reservation')
            ->orderBy($orderBy, $order);

        if ($reservationId !== null) {
            $query = $query->where('reservation_id', $reservationId);
        }
        if ($status !== null) {
            $query = $query->where('status', $status);
        }
        if ($stakeholder !== null) {
            $query = $query->where('stakeholder', $stakeholder);
        }

        return $query->limit($limit)->offset($offset)->fetchAll();
    }

    /**
     * Find notifications older than the given cutoff date, with reservations eagerly loaded.
     *
     * @return Notification[]
     */
    public function findOlderThan(\DateTimeImmutable $cutoff, int $limit = 200): array
    {
        return $this->select()
            ->load('reservation')
            ->where('send_time', '<', $cutoff)
            ->limit($limit)
            ->fetchAll();
    }

    /**
     * Count notifications with optional filters.
     */
    public function countFiltered(
        ?int $reservationId,
        ?string $status,
        ?string $stakeholder,
    ): int {
        $query = $this->select();

        if ($reservationId !== null) {
            $query = $query->where('reservation_id', $reservationId);
        }
        if ($status !== null) {
            $query = $query->where('status', $status);
        }
        if ($stakeholder !== null) {
            $query = $query->where('stakeholder', $stakeholder);
        }

        return $query->count();
    }
}
