<?php

namespace App\Http\Controllers;

use App\Models\PersonalAccessToken;
use App\Services\ActiveRoleService;
use App\Support\AuthCookie;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Modules\Auth\Entities\User;
use Modules\Auth\Transformers\UserResource;

class ImpersonateController extends Controller
{
    private const IMPERSONATION_TOKEN = 'impersonation-token';

    private const AUTH_TOKEN_NAMES = ['authToken', 'auth-token'];

    public function take(Request $request, string $id): JsonResponse
    {
        $target = User::findOrFail($id);
        $admin = $request->user();

        if (! $admin->can('impersonate.user', $target)) {
            return $this->error('Anda tidak mempunyai keizinan untuk menyamar pengguna.', 403);
        }

        if ($request->hasCookie(AuthCookie::originalUserCookieName())) {
            return $this->error('Anda sudah menyamar sebagai pengguna.', 400);
        }

        $issued = $this->issueToken($target, self::IMPERSONATION_TOKEN);

        return AuthCookie::attachAuthToken(
            response()->json([
                'success' => true,
                'message' => "Anda sekarang menyamar sebagai {$target->name}",
                'impersonated_user' => $this->userSummary($target),
                ...$this->sessionPayload($target, $issued['accessToken']),
            ]),
            $issued['plainTextToken']
        )->withCookie(AuthCookie::makeOriginalUserId($admin->id));
    }

    public function leave(Request $request): JsonResponse
    {
        $originalUserId = $request->cookie(AuthCookie::originalUserCookieName());

        if (! $originalUserId) {
            return $this->error('Maklumat pengguna asal tidak ditemui.', 400);
        }

        $original = User::findOrFail($originalUserId);

        $request->user()?->tokens()->where('name', self::IMPERSONATION_TOKEN)->delete();
        $original->tokens()->whereIn('name', [self::IMPERSONATION_TOKEN, ...self::AUTH_TOKEN_NAMES])->delete();

        $issued = $this->issueToken($original, 'auth-token', expires: true);

        $response = response()->json([
            'success' => true,
            'message' => 'Anda telah kembali ke akaun anda',
            'original_user' => $this->userSummary($original),
            ...$this->sessionPayload($original, $issued['accessToken']),
        ]);

        if (AuthCookie::shouldExposeTokenInResponse()) {
            $response->setData(array_merge($response->getData(true), [
                'original_token' => $issued['plainTextToken'],
            ]));
        }

        return AuthCookie::attachAuthToken($response, $issued['plainTextToken'])
            ->withCookie(AuthCookie::forgetOriginalUserId());
    }

    public function status(Request $request): JsonResponse
    {
        if (! $request->hasCookie(AuthCookie::originalUserCookieName())) {
            return response()->json(['is_impersonating' => false]);
        }

        $user = $request->user();

        return response()->json([
            'is_impersonating' => true,
            'impersonated_user' => $user ? $this->userSummary($user) : null,
        ]);
    }

    /**
     * @return array{plainTextToken: string, accessToken: PersonalAccessToken}
     */
    private function issueToken(User $user, string $name, bool $expires = false): array
    {
        $user->loadMissing(['roles.permissions']);

        $result = $user->createToken(
            $name,
            ['*'],
            $expires ? now()->addMinutes((int) config('auth_cookie.lifetime_minutes', 720)) : null
        );

        ActiveRoleService::assignDefaultToToken($user, $result->accessToken);

        return [
            'plainTextToken' => $result->plainTextToken,
            'accessToken' => $result->accessToken,
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function sessionPayload(User $user, PersonalAccessToken $accessToken): array
    {
        $user->loadMissing(['roles.permissions']);

        $activeRole = $user->roles->firstWhere('id', $accessToken->active_role_id)
            ?? ActiveRoleService::resolveDefaultRole($user);

        return [
            'data' => new UserResource($user),
            'active_role' => ActiveRoleService::formatRole($activeRole),
            'can_switch_role' => $user->roles->count() > 1,
            'redirect_path' => $activeRole
                ? ActiveRoleService::redirectPathForRole($activeRole)
                : config('active_role.member_redirect', '/profile'),
        ];
    }

    /**
     * @return array{id: mixed, name: string, email: string}
     */
    private function userSummary(User $user): array
    {
        return [
            'id' => $user->id,
            'name' => $user->name,
            'email' => $user->email,
        ];
    }

    private function error(string $message, int $status): JsonResponse
    {
        return response()->json([
            'success' => false,
            'message' => $message,
        ], $status);
    }
}
