<?php

declare(strict_types=1);

namespace App\Administration\Infrastructure\ReadModel;

use App\Administration\Application\Dto\PaginatedResult;
use App\Administration\Application\Port\PaginatedAssignmentsReader;
use App\Administration\Application\Query\GetAllAssignments\AssignmentWithNamesDto;
use DateTimeImmutable;
use Doctrine\DBAL\Connection;

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

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

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

        $fromClause = <<<SQL
            FROM teacher_assignments ta
            JOIN users u ON u.id = ta.teacher_id AND u.tenant_id = ta.tenant_id
            JOIN school_classes sc ON sc.id = ta.school_class_id AND sc.tenant_id = ta.tenant_id
            JOIN subjects s ON s.id = ta.subject_id AND s.tenant_id = ta.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
                ta.id, ta.teacher_id,
                u.first_name AS teacher_first_name, u.last_name AS teacher_last_name,
                ta.school_class_id AS class_id, sc.name AS class_name,
                ta.subject_id, s.name AS subject_name,
                ta.academic_year_id, ta.status, ta.start_date, ta.end_date, ta.created_at
            {$fromClause}
            WHERE {$whereClause}
            ORDER BY u.last_name ASC, u.first_name ASC, 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): AssignmentWithNamesDto {
            /** @var string $id */
            $id = $row['id'];
            /** @var string $teacherId */
            $teacherId = $row['teacher_id'];
            /** @var string $teacherFirstName */
            $teacherFirstName = $row['teacher_first_name'];
            /** @var string $teacherLastName */
            $teacherLastName = $row['teacher_last_name'];
            /** @var string $classId */
            $classId = $row['class_id'];
            /** @var string $className */
            $className = $row['class_name'];
            /** @var string $subjectId */
            $subjectId = $row['subject_id'];
            /** @var string $subjectName */
            $subjectName = $row['subject_name'];
            /** @var string $academicYearId */
            $academicYearId = $row['academic_year_id'];
            /** @var string $status */
            $status = $row['status'];
            /** @var string $startDate */
            $startDate = $row['start_date'];
            /** @var string|null $endDate */
            $endDate = $row['end_date'];
            /** @var string $createdAt */
            $createdAt = $row['created_at'];

            return new AssignmentWithNamesDto(
                id: $id,
                teacherId: $teacherId,
                teacherFirstName: $teacherFirstName,
                teacherLastName: $teacherLastName,
                classId: $classId,
                className: $className,
                subjectId: $subjectId,
                subjectName: $subjectName,
                academicYearId: $academicYearId,
                status: $status,
                startDate: new DateTimeImmutable($startDate),
                endDate: $endDate !== null ? new DateTimeImmutable($endDate) : null,
                createdAt: new DateTimeImmutable($createdAt),
            );
        }, $rows);

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