<?php

declare(strict_types=1);

namespace App\Administration\Infrastructure\ReadModel;

use App\Administration\Application\Dto\PaginatedResult;
use App\Administration\Application\Port\PaginatedClassesReader;
use App\Administration\Application\Query\GetClasses\ClassDto;
use App\Administration\Domain\Model\SchoolClass\ClassStatus;
use DateTimeImmutable;
use Doctrine\DBAL\Connection;

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

    /**
     * @return PaginatedResult<ClassDto>
     */
    public function findPaginated(
        string $tenantId,
        string $academicYearId,
        ?string $search,
        int $page,
        int $limit,
    ): PaginatedResult {
        $params = [
            'tenant_id' => $tenantId,
            'academic_year_id' => $academicYearId,
            'status' => ClassStatus::ACTIVE->value,
        ];
        $whereClause = 'sc.tenant_id = :tenant_id AND sc.academic_year_id = :academic_year_id AND sc.status = :status';

        if ($search !== null && $search !== '') {
            $whereClause .= ' AND (sc.name ILIKE :search OR sc.level ILIKE :search)';
            $params['search'] = '%' . $search . '%';
        }

        $countSql = "SELECT COUNT(*) FROM school_classes sc WHERE {$whereClause}";

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

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

        $selectSql = <<<SQL
            SELECT sc.id, sc.name, sc.level, sc.capacity, sc.status, sc.description, sc.created_at, sc.updated_at
            FROM school_classes sc
            WHERE {$whereClause}
            ORDER BY sc.name ASC
            LIMIT :limit OFFSET :offset
        SQL;

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

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

        $items = array_map(static function (array $row): ClassDto {
            /** @var string $id */
            $id = $row['id'];
            /** @var string $name */
            $name = $row['name'];
            /** @var string|null $level */
            $level = $row['level'];
            /** @var int|string|null $capacityRaw */
            $capacityRaw = $row['capacity'];
            $capacity = $capacityRaw !== null ? (int) $capacityRaw : null;
            /** @var string $status */
            $status = $row['status'];
            /** @var string|null $description */
            $description = $row['description'];
            /** @var string $createdAt */
            $createdAt = $row['created_at'];
            /** @var string $updatedAt */
            $updatedAt = $row['updated_at'];

            return new ClassDto(
                id: $id,
                name: $name,
                level: $level,
                capacity: $capacity,
                status: $status,
                description: $description,
                createdAt: new DateTimeImmutable($createdAt),
                updatedAt: new DateTimeImmutable($updatedAt),
            );
        }, $rows);

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