<?php

namespace App\Services\Player;

use App\Models\Player;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;

final class PlayerPasswordService
{
    public function change(Player $player, string $currentPassword, string $newPassword): Player
    {
        return DB::transaction(function () use ($player, $currentPassword, $newPassword): Player {
            $locked = Player::query()->lockForUpdate()->findOrFail($player->id);
            $this->assertPasswordManagedLocally($locked);

            if (! is_string($locked->password_hash) || ! Hash::check($currentPassword, $locked->password_hash)) {
                throw ValidationException::withMessages([
                    'current_password' => ['current_password_invalid'],
                ]);
            }

            if (Hash::check($newPassword, $locked->password_hash)) {
                throw ValidationException::withMessages([
                    'password' => ['new_password_must_differ'],
                ]);
            }

            return $this->persist($locked, $newPassword);
        });
    }

    public function reset(Player $player, string $newPassword): Player
    {
        return DB::transaction(function () use ($player, $newPassword): Player {
            $locked = Player::query()->lockForUpdate()->findOrFail($player->id);
            $this->assertPasswordManagedLocally($locked);

            return $this->persist($locked, $newPassword);
        });
    }

    private function assertPasswordManagedLocally(Player $player): void
    {
        if (! $player->isLotteryNative()) {
            throw ValidationException::withMessages([
                'password' => ['native_password_unavailable'],
            ]);
        }
    }

    private function persist(Player $player, string $newPassword): Player
    {
        $player->forceFill([
            'password_hash' => Hash::make($newPassword),
            'native_token_version' => (int) $player->native_token_version + 1,
            'login_failed_count' => 0,
            'login_locked_until' => null,
        ])->save();

        return $player->refresh();
    }
}
