<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

/**
 * Single-Session Model
 * 
 * Manages individual device sessions for users.
 * Only ONE active session per user is allowed at a time.
 * Login on a different device is blocked until current session is invalidated.
 * 
 * Key behaviors:
 * - createSession() allows same-device re-login (upsert)
 * - Login from different device is blocked at controller level
 * - forceLogout() invalidates all sessions
 */
class UserSession extends Model
{
    use HasFactory;

    protected $fillable = [
        'user_id',
        'device_id',
        'jwt_token',
        'token_hash',
        'device_name',
        'device_model',
        'app_version',
        'last_active_at',
        'is_active',
        'expires_at',
    ];

    protected $casts = [
        'is_active' => 'boolean',
        'last_active_at' => 'datetime',
        'expires_at' => 'datetime',
    ];

    /**
     * Get the user that owns this session
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    /**
     * Create a new session for a user/device combination.
     * 
     * SINGLE-SESSION: Only one device at a time.
     * If the same device logs in again, the existing session is refreshed.
     * Login from a different device is blocked at the controller level.
     */
    public static function createSession(
        int $userId,
        string $deviceId,
        string $token,
        ?string $deviceName = null,
        ?string $deviceModel = null,
        ?string $appVersion = null,
        ?int $expiresInDays = 30
    ): self {
        $tokenHash = hash('sha256', $token);

        // Single-session safety: invalidate any other device sessions 
        // (controller already blocks different-device login, this is a safeguard)
        self::where('user_id', $userId)
            ->where('device_id', '!=', $deviceId)
            ->where('is_active', true)
            ->update(['is_active' => false]);

        // Check if this device already has a session for this user
        $existingSession = self::where('user_id', $userId)
            ->where('device_id', $deviceId)
            ->first();

        if ($existingSession) {
            // Update existing session for this device
            $existingSession->update([
                'jwt_token' => $token,
                'token_hash' => $tokenHash,
                'device_name' => $deviceName ?? $existingSession->device_name,
                'device_model' => $deviceModel ?? $existingSession->device_model,
                'app_version' => $appVersion ?? $existingSession->app_version,
                'last_active_at' => now(),
                'is_active' => true,
                'expires_at' => now()->addDays($expiresInDays),
            ]);
            return $existingSession;
        }

        // Create new session for this device
        return self::create([
            'user_id' => $userId,
            'device_id' => $deviceId,
            'jwt_token' => $token,
            'token_hash' => $tokenHash,
            'device_name' => $deviceName,
            'device_model' => $deviceModel,
            'app_version' => $appVersion,
            'last_active_at' => now(),
            'is_active' => true,
            'expires_at' => now()->addDays($expiresInDays),
        ]);
    }

    /**
     * Find a session by token
     */
    public static function findByToken(string $token): ?self
    {
        $tokenHash = hash('sha256', $token);
        return self::where('token_hash', $tokenHash)
            ->where('is_active', true)
            ->first();
    }

    /**
     * Check if session is valid (active and not expired)
     */
    public function isValid(): bool
    {
        if (!$this->is_active) {
            return false;
        }

        if ($this->expires_at && $this->expires_at->isPast()) {
            return false;
        }

        return true;
    }

    /**
     * Update last active timestamp
     */
    public function touch($attribute = null): bool
    {
        $this->last_active_at = now();

        // If specific attribute requested, use parent behavior
        if ($attribute) {
            return parent::touch($attribute);
        }

        return $this->save();
    }

    /**
     * Invalidate this session (logout)
     */
    public function invalidate(): bool
    {
        $this->is_active = false;
        return $this->save();
    }

    /**
     * Get all active sessions for a user
     */
    public static function getActiveSessionsForUser(int $userId): \Illuminate\Database\Eloquent\Collection
    {
        return self::where('user_id', $userId)
            ->where('is_active', true)
            ->where(function ($query) {
                $query->whereNull('expires_at')
                    ->orWhere('expires_at', '>', now());
            })
            ->orderBy('last_active_at', 'desc')
            ->get();
    }

    /**
     * Invalidate session for a specific device
     */
    public static function invalidateByDevice(int $userId, string $deviceId): bool
    {
        return self::where('user_id', $userId)
            ->where('device_id', $deviceId)
            ->update(['is_active' => false]) > 0;
    }

    /**
     * Invalidate all sessions for a user except the current one
     */
    public static function invalidateAllExcept(int $userId, int $exceptSessionId): int
    {
        return self::where('user_id', $userId)
            ->where('id', '!=', $exceptSessionId)
            ->where('is_active', true)
            ->update(['is_active' => false]);
    }

    /**
     * Invalidate all sessions for a user
     */
    public static function invalidateAll(int $userId): int
    {
        return self::where('user_id', $userId)
            ->where('is_active', true)
            ->update(['is_active' => false]);
    }
}
