<?php
declare(strict_types=1);

namespace App\Modules\Users;

use PDO;

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

    /**
     * @return array<int, array<string, mixed>>
     */
    public function all(): array
    {
        $statement = $this->pdo->query('SELECT id, name, email, created_at FROM users ORDER BY id DESC');
        $rows = $statement->fetchAll();

        if (!is_array($rows)) {
            return [];
        }

        return array_map(
            static fn (array $row): array => [
                'id' => (int) ($row['id'] ?? 0),
                'name' => (string) ($row['name'] ?? ''),
                'email' => (string) ($row['email'] ?? ''),
                'created_at' => (string) ($row['created_at'] ?? ''),
            ],
            $rows
        );
    }

    /**
     * @param array<string, mixed> $filters
     * @return array{items:array<int, array<string, mixed>>, total:int, page:int, per_page:int}
     */
    public function paginate(array $filters): array
    {
        $page = max(1, (int) ($filters['page'] ?? 1));
        $perPage = max(1, min(100, (int) ($filters['per_page'] ?? 20)));
        $offset = ($page - 1) * $perPage;

        $where = [];
        $params = [];

        $search = trim((string) ($filters['search'] ?? ''));
        if ($search !== '') {
            $where[] = '(name LIKE :search OR email LIKE :search)';
            $params['search'] = '%' . $search . '%';
        }

        $whereSql = $where === [] ? '' : (' WHERE ' . implode(' AND ', $where));

        $sort = (string) ($filters['sort'] ?? 'id');
        $sortDir = strtoupper((string) ($filters['sort_dir'] ?? 'DESC')) === 'ASC' ? 'ASC' : 'DESC';
        $sortColumn = match ($sort) {
            'name' => 'name',
            'email' => 'email',
            'created_at' => 'created_at',
            default => 'id',
        };

        $countStmt = $this->pdo->prepare('SELECT COUNT(*) FROM users' . $whereSql);
        $countStmt->execute($params);
        $total = (int) $countStmt->fetchColumn();

        $listStmt = $this->pdo->prepare(
            'SELECT id, name, email, created_at FROM users' . $whereSql
            . ' ORDER BY ' . $sortColumn . ' ' . $sortDir
            . ' LIMIT :limit OFFSET :offset'
        );
        foreach ($params as $key => $value) {
            $listStmt->bindValue(':' . $key, $value);
        }
        $listStmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
        $listStmt->bindValue(':offset', $offset, PDO::PARAM_INT);
        $listStmt->execute();

        $rows = $listStmt->fetchAll();
        if (!is_array($rows)) {
            $rows = [];
        }

        return [
            'items' => array_map(
                static fn (array $row): array => [
                    'id' => (int) ($row['id'] ?? 0),
                    'name' => (string) ($row['name'] ?? ''),
                    'email' => (string) ($row['email'] ?? ''),
                    'created_at' => (string) ($row['created_at'] ?? ''),
                ],
                $rows
            ),
            'total' => $total,
            'page' => $page,
            'per_page' => $perPage,
        ];
    }

    public function hasAny(): bool
    {
        $statement = $this->pdo->query('SELECT 1 FROM users LIMIT 1');
        return $statement->fetchColumn() !== false;
    }

    /**
     * @return array<string, mixed>|null
     */
    public function findByEmail(string $email): ?array
    {
        $normalizedEmail = strtolower(trim($email));
        if ($normalizedEmail === '') {
            return null;
        }

        $statement = $this->pdo->prepare(
            'SELECT id, name, email, password_hash, created_at FROM users WHERE email = :email LIMIT 1'
        );
        $statement->execute(['email' => $normalizedEmail]);

        $row = $statement->fetch();
        if (!is_array($row)) {
            return null;
        }

        return [
            'id' => (int) ($row['id'] ?? 0),
            'name' => (string) ($row['name'] ?? ''),
            'email' => (string) ($row['email'] ?? ''),
            'password_hash' => (string) ($row['password_hash'] ?? ''),
            'created_at' => (string) ($row['created_at'] ?? ''),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    public function create(string $name, string $email, string $passwordHash): array
    {
        $createdAt = date('Y-m-d H:i:s');
        $normalizedName = trim($name);
        $normalizedEmail = strtolower(trim($email));

        $statement = $this->pdo->prepare(
            'INSERT INTO users (name, email, password_hash, created_at) VALUES (:name, :email, :password_hash, :created_at)'
        );
        $statement->execute([
            'name' => $normalizedName,
            'email' => $normalizedEmail,
            'password_hash' => $passwordHash,
            'created_at' => $createdAt,
        ]);

        $id = (int) $this->pdo->lastInsertId();
        $record = [
            'id' => $id,
            'name' => $normalizedName,
            'email' => $normalizedEmail,
            'password_hash' => $passwordHash,
            'created_at' => $createdAt,
        ];

        return $record;
    }

}
