<?php

declare(strict_types=1);

namespace App\Services;

use App\enum\UserLevel;
use App\Models\ImpersonationLog;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;

final class ImpersonationService
{
    private const IMPERSONATION_SESSION_KEY = 'impersonation';

    private const IMPERSONATION_START_TIME_KEY = 'impersonation_start_time';

    private const ORIGINAL_USER_KEY = 'original_admin_user_id';

    private const IMPERSONATION_TIMEOUT = 30; // minutes

    public function canImpersonate(User $admin, User $target): bool
    {
        // Only SUPERADMIN can impersonate
        if ($admin->level !== UserLevel::SUPERADMIN) {
            return false;
        }

        // Cannot impersonate other SUPERADMIN users
        if ($target->level === UserLevel::SUPERADMIN) {
            return false;
        }

        // Cannot impersonate self
        if ($admin->id === $target->id) {
            return false;
        }

        // Can only impersonate NORMALUSER
        if ($target->level !== UserLevel::NORMALUSER) {
            return false;
        }

        // Check if admin is already impersonating someone
        if ($this->isImpersonating()) {
            return false;
        }

        return true;
    }

    public function isImpersonating(): bool
    {
        return Session::has(self::IMPERSONATION_SESSION_KEY) &&
               Session::has(self::ORIGINAL_USER_KEY);
    }

    public function getCurrentImpersonator(): ?User
    {
        if (! $this->isImpersonating()) {
            return null;
        }

        $originalUserId = Session::get(self::ORIGINAL_USER_KEY);

        return User::find($originalUserId);
    }

    public function getImpersonationStartTime(): ?Carbon
    {
        if (! $this->isImpersonating()) {
            return null;
        }

        return Session::get(self::IMPERSONATION_START_TIME_KEY);
    }

    public function isImpersonationExpired(): bool
    {
        if (! $this->isImpersonating()) {
            return false;
        }

        $startTime = $this->getImpersonationStartTime();

        if (! $startTime) {
            return true;
        }

        return $startTime->diffInMinutes(now()) > self::IMPERSONATION_TIMEOUT;
    }

    public function startImpersonation(User $admin, User $target, Request $request): bool
    {
        if (! $this->canImpersonate($admin, $target)) {
            return false;
        }

        // Store original admin user info
        Session::put(self::ORIGINAL_USER_KEY, $admin->id);
        Session::put(self::IMPERSONATION_START_TIME_KEY, now());

        // Log the impersonation start
        $impersonationLog = ImpersonationLog::create([
            'admin_id' => $admin->id,
            'target_user_id' => $target->id,
            'start_time' => now(),
            'ip_address' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'status' => 'active',
        ]);

        Session::put(self::IMPERSONATION_SESSION_KEY, $impersonationLog->id);

        // Log out the admin and log in as target user
        Auth::logout();
        Auth::login($target);

        // Regenerate session for security
        Session::regenerate(true);

        return true;
    }

    public function stopImpersonation(Request $request): bool
    {
        if (! $this->isImpersonating()) {
            return false;
        }

        $admin = $this->getCurrentImpersonator();
        $target = Auth::user();
        $impersonationLogId = Session::get(self::IMPERSONATION_SESSION_KEY);

        // Update the impersonation log
        if ($impersonationLogId) {
            $log = ImpersonationLog::find($impersonationLogId);
            if ($log) {
                $log->update([
                    'end_time' => now(),
                    'status' => 'completed',
                ]);
            }
        }

        // Clear impersonation session data
        Session::forget([
            self::IMPERSONATION_SESSION_KEY,
            self::IMPERSONATION_START_TIME_KEY,
            self::ORIGINAL_USER_KEY,
        ]);

        // Log out target user and log back in as admin
        Auth::logout();

        if ($admin) {
            Auth::login($admin);
        }

        // Regenerate session for security
        Session::regenerate(true);

        return true;
    }

    public function forceStopAllImpersonations(): void
    {
        // Force end any active impersonation sessions in the database
        ImpersonationLog::where('status', 'active')
            ->whereNull('end_time')
            ->update([
                'end_time' => now(),
                'status' => 'force_terminated',
            ]);
    }

    public function getRemainingMinutes(): int
    {
        if (! $this->isImpersonating()) {
            return 0;
        }

        $startTime = $this->getImpersonationStartTime();

        if (! $startTime) {
            return 0;
        }

        $elapsed = $startTime->diffInMinutes(now());
        $remaining = self::IMPERSONATION_TIMEOUT - $elapsed;

        return (int) max(0, $remaining);
    }
}
