<?php

namespace App\Master\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Master\Http\Requests\SiteRequest;
use App\Master\Http\Requests\StoreAllUserRequest;
use App\Master\Http\Resources\UserListResource;
use App\Master\Models\ImpersonationRequest;
use App\Master\Http\Resources\UserViewResource;
use App\Notifications\PasswordResetNotification;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Mtc\MercuryDataModels\Permission;
use Mtc\MercuryDataModels\TwoFactorRememberedDevice;
use Mtc\MercuryDataModels\User;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

class AllUserController extends Controller
{
    public function __construct()
    {
        $this->middleware([
            'permission:manage-sites'
        ]);
    }

    protected function authorizeUserAction(User $targetUser): void
    {
        if ($targetUser->hasRole('mtc') && !Auth::user()->hasPermissionTo('system-owner')) {
            throw new AccessDeniedHttpException('You do not have permission to manage this user.');
        }
    }

    /**
     * Display a list of users in system.
     *
     * @return UserListResource
     */
    public function index(SiteRequest $request)
    {
        $term = $request->input('term');
        $currentUserId = Auth::id();
        $sessionId = session()->getId();

        $users = User::query()
            ->when(
                $request->filled('term'),
                fn ($query) => $query->where(fn ($termQuery) => $termQuery->where('name', 'like', "%$term%")
                    ->orWhere('email', 'like', "%$term%"))
            )
            ->when(
                $request->filled('role'),
                fn ($query) => $query->whereIn('id', function ($subquery) use ($request) {
                    $subquery->select('model_id')
                        ->from('model_has_roles')
                        ->join('roles', 'roles.id', '=', 'model_has_roles.role_id')
                        ->where('roles.name', $request->input('role'));
                })
            )
            ->orderBy('name')
            ->with([
                'tenants',
            ])
            ->paginate($request->input('per_page', 15));

        $targetUserIds = $users->pluck('id')->toArray();

        $impersonationStatuses = ImpersonationRequest::query()
            ->where('requester_id', $currentUserId)
            ->whereIn('target_user_id', $targetUserIds)
            ->whereIn('status', ['pending', 'approved'])
            ->get()
            ->groupBy('target_user_id')
            ->map(function ($requests) use ($sessionId) {
                $hasPending = $requests->where('status', 'pending')->isNotEmpty();
                $hasUsable = $requests
                    ->where('status', 'approved')
                    ->where('session_id', $sessionId)
                    ->filter(fn ($r) => !$r->isExpired() && !$r->isUsed())
                    ->isNotEmpty();

                return [
                    'has_pending_request' => $hasPending,
                    'has_usable_request' => $hasUsable,
                ];
            });

        return new UserListResource($users, $impersonationStatuses);
    }

    /**
     * Create a new user with the mtc role.
     *
     * Only system owners may create mtc users. A random password is generated
     * and the user is emailed a reset notification, mirroring resetPassword().
     */
    public function store(StoreAllUserRequest $request)
    {
        if (!Auth::user()->hasPermissionTo('system-owner')) {
            throw new AccessDeniedHttpException('You do not have permission to create users.');
        }

        $password = Str::random(12);

        /** @var User $user */
        $user = User::query()->create([
            'name' => $request->input('name'),
            'email' => $request->input('email'),
            'password' => bcrypt($password),
            'requires_password_change' => true,
        ]);

        $user->assignRole(config('mtc_login.mtc_user_role'));
        $user->notify(new PasswordResetNotification($password));

        return (new UserViewResource($user))
            ->response()
            ->setStatusCode(201);
    }

    /**
     * Display the specified resource.
     *
     * @param User $allUser
     * @return UserViewResource
     */
    public function show(User $allUser)
    {
        $this->authorizeUserAction($allUser);

        return new UserViewResource($allUser);
    }

    public function destroy(User $allUser)
    {
        $this->authorizeUserAction($allUser);

        $allUser->delete();
    }

    public function reset2FactorAuth(User $user)
    {
        $this->authorizeUserAction($user);

        $user->two_factor_secret = null;
        $user->two_factor_recovery_codes = null;
        $user->two_factor_confirmed_at = null;
        $user->save();

        TwoFactorRememberedDevice::query()
            ->where('user_id', $user->id)
            ->delete();

        return new UserViewResource($user);
    }

    public function requirePasswordChange(User $user)
    {
        $this->authorizeUserAction($user);

        $user->update([
            'requires_password_change' => true,
        ]);

        return new UserViewResource($user);
    }

    public function resetPassword(User $user)
    {
        $this->authorizeUserAction($user);

        $password = Str::random(12);
        $user->update([
            'password' => bcrypt($password),
        ]);

        $user->notify(new PasswordResetNotification($password));
        return new UserViewResource($user);
    }

    public function togglePermission(User $user, Request $request)
    {
        $permissionName = $request->input('permission');

        if (!Auth::user()->hasPermissionTo('system-owner')) {
            throw new AccessDeniedHttpException('You do not have permission to manage user permissions.');
        }

        if ($permissionName === 'system-owner' && !$user->hasRole('mtc')) {
            throw new AccessDeniedHttpException('System owner permission can only be set for users with MTC role.');
        }

        $permission = Permission::findByName($permissionName, 'web');

        if ($request->input('enabled')) {
            $user->givePermissionTo($permission);
        } else {
            $user->revokePermissionTo($permission);
        }

        return new UserViewResource($user);
    }
}
