<?php

namespace App\Services;

use App\Exceptions\ImpersonationException;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;

class ImpersonationService
{
    protected const SESSION_KEY = 'impersonating_from';

    public function __construct(
        protected AuditLogService $auditLogService
    ) {}

    /**
     * Start impersonating a user.
     *
     * @throws ImpersonationException
     */
    public function start(User $admin, User $target): bool
    {
        // Validate admin can impersonate
        if (! $admin->isAdmin()) {
            throw new ImpersonationException('Only admins can impersonate users');
        }

        // Cannot impersonate self
        if ($admin->id === $target->id) {
            throw new ImpersonationException('Cannot impersonate yourself');
        }

        // Cannot impersonate other admins
        if ($target->isAdmin()) {
            throw new ImpersonationException('Cannot impersonate admin users');
        }

        // Cannot impersonate inactive users
        if ($target->status === 'inactive') {
            throw new ImpersonationException('Cannot impersonate inactive users');
        }

        // Cannot nest impersonation
        if ($this->isImpersonating()) {
            throw new ImpersonationException('Already impersonating a user');
        }

        // Store original admin ID in session
        Session::put(self::SESSION_KEY, $admin->id);

        // Log the impersonation start
        $this->auditLogService->log(
            action: 'impersonation_started',
            user: $target,
            actor: $admin,
            entityType: 'User',
            entityId: $target->id,
            metadata: [
                'impersonated_user_email' => $target->email,
                'started_at' => now()->toIso8601String(),
            ]
        );

        // Switch to the target user
        Auth::login($target);

        return true;
    }

    /**
     * Stop impersonating and return to original admin.
     */
    public function stop(): bool
    {
        if (! $this->isImpersonating()) {
            return false;
        }

        $adminId = Session::get(self::SESSION_KEY);
        $admin = User::find($adminId);
        $impersonatedUser = Auth::user();

        if (! $admin) {
            // Clear session and return false if admin not found
            Session::forget(self::SESSION_KEY);

            return false;
        }

        // Log the impersonation stop
        $this->auditLogService->log(
            action: 'impersonation_stopped',
            user: $impersonatedUser,
            actor: $admin,
            entityType: 'User',
            entityId: $impersonatedUser->id,
            metadata: [
                'impersonated_user_email' => $impersonatedUser->email,
                'stopped_at' => now()->toIso8601String(),
            ]
        );

        // Clear the impersonation session
        Session::forget(self::SESSION_KEY);

        // Log back in as admin
        Auth::login($admin);

        return true;
    }

    /**
     * Check if currently impersonating a user.
     */
    public function isImpersonating(): bool
    {
        return Session::has(self::SESSION_KEY);
    }

    /**
     * Get the original admin user (the impersonator).
     */
    public function getImpersonator(): ?User
    {
        if (! $this->isImpersonating()) {
            return null;
        }

        return User::find(Session::get(self::SESSION_KEY));
    }

    /**
     * Get the impersonated user (current auth user when impersonating).
     */
    public function getImpersonatedUser(): ?User
    {
        if (! $this->isImpersonating()) {
            return null;
        }

        return Auth::user();
    }
}
