<?php

declare(strict_types=1);

namespace App\Administration\Infrastructure\ReadModel;

use App\Administration\Application\Dto\PaginatedResult;
use App\Administration\Application\Port\PaginatedParentInvitationsReader;
use App\Administration\Application\Query\GetParentInvitations\ParentInvitationDto;
use DateTimeImmutable;
use Doctrine\DBAL\Connection;

final readonly class DbalPaginatedParentInvitationsReader implements PaginatedParentInvitationsReader
{
    public function __construct(
        private Connection $connection,
    ) {
    }

    /**
     * @return PaginatedResult<ParentInvitationDto>
     */
    public function findPaginated(
        string $tenantId,
        ?string $status,
        ?string $studentId,
        ?string $search,
        int $page,
        int $limit,
    ): PaginatedResult {
        $params = ['tenant_id' => $tenantId];
        $whereClause = 'pi.tenant_id = :tenant_id';

        if ($status !== null) {
            $whereClause .= ' AND pi.status = :status';
            $params['status'] = $status;
        }

        if ($studentId !== null) {
            $whereClause .= ' AND pi.student_id = :student_id';
            $params['student_id'] = $studentId;
        }

        if ($search !== null && $search !== '') {
            $whereClause .= ' AND (pi.parent_email ILIKE :search OR u.first_name ILIKE :search OR u.last_name ILIKE :search)';
            $params['search'] = '%' . $search . '%';
        }

        $fromClause = <<<SQL
            FROM parent_invitations pi
            LEFT JOIN users u ON u.id = pi.student_id AND u.tenant_id = pi.tenant_id
        SQL;

        $countSql = "SELECT COUNT(*) {$fromClause} WHERE {$whereClause}";

        /** @var int|string|false $totalRaw */
        $totalRaw = $this->connection->fetchOne($countSql, $params);
        $total = (int) $totalRaw;

        $offset = ($page - 1) * $limit;

        $selectSql = <<<SQL
            SELECT
                pi.id, pi.student_id, pi.parent_email, pi.status,
                pi.created_at, pi.expires_at, pi.sent_at, pi.activated_at, pi.activated_user_id,
                u.first_name AS student_first_name, u.last_name AS student_last_name
            {$fromClause}
            WHERE {$whereClause}
            ORDER BY pi.created_at DESC
            LIMIT :limit OFFSET :offset
        SQL;

        $params['limit'] = $limit;
        $params['offset'] = $offset;

        $rows = $this->connection->fetchAllAssociative($selectSql, $params);

        $items = array_map(static function (array $row): ParentInvitationDto {
            /** @var string $id */
            $id = $row['id'];
            /** @var string $studentId */
            $studentId = $row['student_id'];
            /** @var string $parentEmail */
            $parentEmail = $row['parent_email'];
            /** @var string $status */
            $status = $row['status'];
            /** @var string $createdAt */
            $createdAt = $row['created_at'];
            /** @var string $expiresAt */
            $expiresAt = $row['expires_at'];
            /** @var string|null $sentAt */
            $sentAt = $row['sent_at'];
            /** @var string|null $activatedAt */
            $activatedAt = $row['activated_at'];
            /** @var string|null $activatedUserId */
            $activatedUserId = $row['activated_user_id'];
            /** @var string|null $studentFirstName */
            $studentFirstName = $row['student_first_name'];
            /** @var string|null $studentLastName */
            $studentLastName = $row['student_last_name'];

            return new ParentInvitationDto(
                id: $id,
                studentId: $studentId,
                parentEmail: $parentEmail,
                status: $status,
                createdAt: new DateTimeImmutable($createdAt),
                expiresAt: new DateTimeImmutable($expiresAt),
                sentAt: $sentAt !== null ? new DateTimeImmutable($sentAt) : null,
                activatedAt: $activatedAt !== null ? new DateTimeImmutable($activatedAt) : null,
                activatedUserId: $activatedUserId,
                studentFirstName: $studentFirstName,
                studentLastName: $studentLastName,
            );
        }, $rows);

        return new PaginatedResult(
            items: $items,
            total: $total,
            page: $page,
            limit: $limit,
        );
    }
}
