<?php

declare(strict_types=1);

namespace App\SuperAdmin\Infrastructure\Persistence\Doctrine;

use App\SuperAdmin\Domain\Exception\SuperAdminIntrouvableException;
use App\SuperAdmin\Domain\Model\SuperAdmin\SuperAdmin;
use App\SuperAdmin\Domain\Model\SuperAdmin\SuperAdminId;
use App\SuperAdmin\Domain\Model\SuperAdmin\SuperAdminStatus;
use App\SuperAdmin\Domain\Repository\SuperAdminRepository;
use DateTimeImmutable;
use Doctrine\DBAL\Connection;
use Override;

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

    #[Override]
    public function save(SuperAdmin $superAdmin): void
    {
        $this->connection->executeStatement(
            <<<'SQL'
                INSERT INTO super_admins (id, email, password_hash, first_name, last_name, status, created_at, last_login_at)
                VALUES (:id, :email, :password_hash, :first_name, :last_name, :status, :created_at, :last_login_at)
                ON CONFLICT (id) DO UPDATE SET
                    email = EXCLUDED.email,
                    password_hash = EXCLUDED.password_hash,
                    first_name = EXCLUDED.first_name,
                    last_name = EXCLUDED.last_name,
                    status = EXCLUDED.status,
                    last_login_at = EXCLUDED.last_login_at
            SQL,
            [
                'id' => (string) $superAdmin->id,
                'email' => $superAdmin->email,
                'password_hash' => $superAdmin->hashedPassword,
                'first_name' => $superAdmin->firstName,
                'last_name' => $superAdmin->lastName,
                'status' => $superAdmin->status->value,
                'created_at' => $superAdmin->createdAt->format('Y-m-d H:i:sP'),
                'last_login_at' => $superAdmin->lastLoginAt?->format('Y-m-d H:i:sP'),
            ],
        );
    }

    #[Override]
    public function get(SuperAdminId $id): SuperAdmin
    {
        $row = $this->connection->fetchAssociative(
            'SELECT * FROM super_admins WHERE id = :id',
            ['id' => (string) $id],
        );

        if ($row === false) {
            throw SuperAdminIntrouvableException::avecId($id);
        }

        return $this->hydrate($row);
    }

    #[Override]
    public function findByEmail(string $email): ?SuperAdmin
    {
        $row = $this->connection->fetchAssociative(
            'SELECT * FROM super_admins WHERE email = :email',
            ['email' => $email],
        );

        if ($row === false) {
            return null;
        }

        return $this->hydrate($row);
    }

    /**
     * @param array<string, mixed> $row
     */
    private function hydrate(array $row): SuperAdmin
    {
        /** @var string $id */
        $id = $row['id'];
        /** @var string $email */
        $email = $row['email'];
        /** @var string $passwordHash */
        $passwordHash = $row['password_hash'];
        /** @var string $firstName */
        $firstName = $row['first_name'];
        /** @var string $lastName */
        $lastName = $row['last_name'];
        /** @var string $status */
        $status = $row['status'];
        /** @var string $createdAt */
        $createdAt = $row['created_at'];
        /** @var string|null $lastLoginAt */
        $lastLoginAt = $row['last_login_at'];

        return SuperAdmin::reconstitute(
            id: SuperAdminId::fromString($id),
            email: $email,
            hashedPassword: $passwordHash,
            firstName: $firstName,
            lastName: $lastName,
            status: SuperAdminStatus::from($status),
            createdAt: new DateTimeImmutable($createdAt),
            lastLoginAt: $lastLoginAt !== null ? new DateTimeImmutable($lastLoginAt) : null,
        );
    }
}
