<?php

declare(strict_types=1);

namespace App\Services;

use App\Http\Resources\UserResource;
use App\Models\User;
use App\Models\UserDevice;
use App\Support\ApiResponse;
use Carbon\Carbon;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;

class UserService
{
    public function login(Request $request, $refreshToken = null)
    {
        if ($request->has('email')) {
            $user = User::where('email', $request->email)->first();
        } else {
            $user = User::where('cellphone', $request->cellphone)->first();
        }
        if (! $user) {
            return ApiResponse::response(message: 'کاربری با مشخصات وارد شده یافت نشد', status: 404);
        }

        if ($blocked = $this->blockedResponse($user)) {
            return $blocked;
        }

        $hash_check = Hash::check($request->password, $user->password);
        if (! $hash_check) {
            return ApiResponse::response(message: 'رمز عبور اشتباه است', status: 422);
        }

        $user->update(['last_online_at' => Carbon::now()]);

        return $this->authSuccess($user);
    }

    public function register(Request $request)
    {
        $user = User::where('cellphone', $request->cellphone)->first();
        if ($user) {
            return ApiResponse::response(message: 'شما قبلا ثبت نام کرده اید', status: 409);
        }

        $item = new User;
        $data = $request->except('code', 'role', 'account_type');
        if ($request->filled('password')) {
            $data['password'] = Hash::make($request->password);
        }

        $role = $request->input('role', User::ROLE_EMPLOYER);
        if (! in_array($role, [User::ROLE_EMPLOYER, User::ROLE_USER], true)) {
            $role = User::ROLE_EMPLOYER;
        }

        $data['account_type'] = $role === User::ROLE_EMPLOYER
            ? User::ACCOUNT_EMPLOYER
            : User::ACCOUNT_EMPLOYER;
        $data['status'] = 1;
        $data['is_blocked'] = false;

        $user = $item->create($data);
        $user->syncRoles([$role, User::ROLE_USER]);

        return $this->authSuccess($user);
    }

    public function logout(Request $request)
    {
        Auth::user()->tokens()->delete();

        return ApiResponse::success(message: 'با موفقیت خارج شدید');
    }

    public function forgot(Request $request)
    {
        $user = User::where('cellphone', $request->cellphone)->firstOrFail();

        if (isset($request->newPassword) && isset($request->newPasswordConfirmation)) {
            if ($request->newPassword !== $request->newPasswordConfirmation) {
                return ApiResponse::response(message: 'رمز های وارد شده یکسان نیست!', status: 401);
            }
        }

        return $user->update([
            'password' => ($request->newPassword) ? Hash::make($request->newPassword) : $user->password,
        ]);
    }

    public function authenticate(Request $request)
    {
        try {
            DB::beginTransaction();
            $user = User::where('cellphone', $request->cellphone)->first();
            if (! $user) {
                $item = new User;
                $data = $request->except('code');
                $data['account_type'] = User::ACCOUNT_EMPLOYER;
                $data['status'] = 1;
                $user = $item->create($data);
                $user->syncRoles([User::ROLE_EMPLOYER, User::ROLE_USER]);
            }

            if ($blocked = $this->blockedResponse($user)) {
                DB::rollBack();

                return $blocked;
            }

            if ($request->password) {
                $hash_check = Hash::check($request->password, $user->password);
                if (! $hash_check) {
                    DB::rollBack();

                    return ApiResponse::response(message: 'رمز عبور اشتباه است', status: 422);
                }

                $user->update([
                    'login_type' => 1,
                ]);
            } else {
                $user->update([
                    'login_type' => 0,
                ]);
            }

            $user->refresh();
            if (! count($user->roles)) {
                $user->syncRoles([User::ROLE_EMPLOYER, User::ROLE_USER]);
            }

            DB::commit();

            return $this->authSuccess($user);
        } catch (Exception $exception) {
            DB::rollBack();

            return ApiResponse::response(message: $exception->getMessage(), status: 422);
        }
    }

    public function authSuccess(User $user): JsonResponse
    {
        $tokenData = $this->issueToken($user);
        $this->trackDevice($user, request());
        $user->loadMissing(['roles', 'producer', 'expert', 'province', 'county']);

        $payload = (new UserResource($user))->resolve();
        $payload['token'] = $tokenData['access_token'];
        $payload['access_token'] = $tokenData['access_token'];
        $payload['expires_in'] = $tokenData['expires_in'];

        return ApiResponse::success($payload);
    }

    private function trackDevice(User $user, Request $request): void
    {
        $agent = (string) $request->userAgent();
        $fingerprint = hash('sha256', implode('|', [
            $user->id,
            $agent,
            $request->ip(),
        ]));

        UserDevice::query()
            ->where('user_id', $user->id)
            ->update(['is_current' => false]);

        UserDevice::query()->updateOrCreate(
            [
                'user_id' => $user->id,
                'fingerprint' => $fingerprint,
            ],
            [
                'name' => $request->input('device_name') ?: (mb_substr($agent, 0, 80) ?: 'دستگاه ناشناس'),
                'device_type' => $request->input('device_type'),
                'platform' => $request->input('platform'),
                'browser' => $request->input('browser'),
                'user_agent' => mb_substr($agent, 0, 500),
                'ip' => $request->ip(),
                'is_current' => true,
                'is_blocked' => false,
                'last_used_at' => now(),
            ]
        );
    }

    private function blockedResponse(User $user): ?JsonResponse
    {
        if ($user->isBlocked()) {
            return ApiResponse::response(
                message: $user->blocked_reason ?: $user->status_desc ?: 'حساب کاربری شما مسدود شده است.',
                status: 403
            );
        }

        return null;
    }

    private function issueToken(User $user)
    {
        try {
            $expire_at = now()->addMonth();
            $token = $user->createToken(
                'application',
                ['*'],
                $expire_at
            );

            if (! $token->plainTextToken) {
                throw new Exception('Failed to create access token');
            }

            return [
                'access_token' => $token->plainTextToken,
                'expires_in' => $expire_at->timestamp,
            ];
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }
}
