<?php

namespace App\Services;

use App\Models\PartnerToken;
use App\Models\PresenceFingerprint;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Http;

class PartnerTokenService
{
    /**
     * Generate a new token for partner with showId (permanent, no TTL).
     *
     * @param int $partnerId
     * @param string $showId
     * @return array{token: string, created_at: string, showId: string}
     */
    public function generateToken(int $partnerId, string $showId): array
    {
        $token = $this->generateUniqueToken();

        $partnerToken = PartnerToken::create([
            'token' => $token,
            'partner_id' => $partnerId,
            'show_id' => $showId,
            'is_active' => true,
        ]);

        return [
            'token' => $partnerToken->token,
            'showId' => $partnerToken->show_id,
            'created_at' => $partnerToken->created_at->toISOString(),
        ];
    }

    /**
     * Get token data from database.
     *
     * @param string $token
     * @return array|null
     */
    public function getTokenData(string $token): ?array
    {
        $partnerToken = PartnerToken::where('token', $token)->first();

        if (! $partnerToken) {
            return null;
        }

        // Check if expired
        if ($partnerToken->isExpired()) {
            $this->deleteToken($token);
            return null;
        }

        // Check if active
        if (! $partnerToken->isActive()) {
            return null;
        }

        return [
            'partner_id' => $partnerToken->partner_id,
            'showId' => $partnerToken->show_id,
            'created_at' => $partnerToken->created_at->toISOString(),
            'expires_at' => $partnerToken->expires_at?->toISOString(),
        ];
    }

    /**
     * Get complete token data including presence information.
     *
     * @param string $token
     * @return array|null
     */
    public function getTokenCompleteData(string $token): ?array
    {
        $tokenData = $this->getTokenData($token);

        if (! $tokenData) {
            return null;
        }

        // Get active fingerprints for this token
        $fingerprints = PresenceFingerprint::where('token', $token)
            ->where('expires_at', '>', now())
            ->get();

        $fingerprintData = [];
        foreach ($fingerprints as $fp) {
            $ttl = now()->diffInSeconds($fp->expires_at);
            $fingerprintData[] = [
                'fingerprint' => $fp->fingerprint,
                'ttl' => $ttl,
                'is_active' => true,
            ];
        }

        return [
            'token_data' => $tokenData,
            'active_users' => count($fingerprintData),
            'fingerprints' => $fingerprintData,
        ];
    }

    /**
     * Check if token has active users (online).
     *
     * @param string $token
     * @return bool
     */
    public function isTokenOnline(string $token): bool
    {
        return PresenceFingerprint::where('token', $token)
            ->where('expires_at', '>', now())
            ->exists();
    }

    /**
     * Get all tokens for a partner.
     *
     * @param int $partnerId
     * @return array
     */
    public function getPartnerTokens(int $partnerId): array
    {
        $tokens = PartnerToken::where('partner_id', $partnerId)
            ->orderBy('created_at', 'desc')
            ->get();

        $result = [];
        foreach ($tokens as $token) {
            // Check if expired
            if ($token->isExpired()) {
                $this->deleteToken($token->token);
                continue;
            }

            // Check if token has active users (online)
            $isOnline = $this->isTokenOnline($token->token);

            // Calculate TTL (-1 means permanent/no expiration)
            $ttl = -1;
            if ($token->expires_at) {
                $ttl = now()->diffInSeconds($token->expires_at);
                if ($ttl < 0) {
                    $ttl = -1;
                }
            }

            $result[] = [
                'token' => $token->token,
                'showId' => $token->show_id,
                'created_at' => $token->created_at->toISOString(),
                'expires_at' => $token->expires_at?->toISOString(),
                'ttl' => $ttl,
                'is_online' => $isOnline,
            ];
        }

        return $result;
    }

    /**
     * Delete a token.
     *
     * @param string $token
     * @return bool
     */
    public function deleteToken(string $token): bool
    {
        $partnerToken = PartnerToken::where('token', $token)->first();

        if (! $partnerToken) {
            return false;
        }

        // Delete all associated fingerprints
        PresenceFingerprint::where('token', $token)->delete();

        // Delete the token
        $partnerToken->delete();

        return true;
    }

    /**
     * Delete all tokens for a partner.
     *
     * @param int $partnerId
     * @return int Number of tokens deleted
     */
    public function deletePartnerTokens(int $partnerId): int
    {
        $tokens = PartnerToken::where('partner_id', $partnerId)->get();
        $count = 0;

        foreach ($tokens as $token) {
            if ($this->deleteToken($token->token)) {
                $count++;
            }
        }

        return $count;
    }

    /**
     * Check if token is valid.
     *
     * @param string $token
     * @return bool
     */
    public function isValidToken(string $token): bool
    {
        $data = $this->getTokenData($token);
        return $data !== null;
    }

    /**
     * Reset token access by removing all connected fingerprints.
     * Also notifies WebSocket server to disconnect active users.
     *
     * @param string $token
     * @return bool
     */
    public function resetTokenAccess(string $token): bool
    {
        $tokenData = $this->getTokenData($token);

        if (! $tokenData) {
            return false;
        }

        // Notify WebSocket server to disconnect all users with this token
        $this->notifyWebSocketServerReset($token);

        // Delete all fingerprints connected to this token
        PresenceFingerprint::where('token', $token)->delete();

        return true;
    }

    /**
     * Notify WebSocket server to disconnect users with this token.
     *
     * @param string $token
     * @return void
     */
    protected function notifyWebSocketServerReset(string $token): void
    {
        try {
            $websocketUrl = config('services.websocket.url', env('WEBSOCKET_SERVER_URL', 'https://api.ngidolihub.my.id'));
            $apiUrl = rtrim($websocketUrl, '/') . '/api/token-reset/reset';

            $response = Http::timeout(5)
                ->post($apiUrl, [
                    'token' => $token,
                ]);

            if ($response->successful()) {
                $result = $response->json();
                Log::info('WebSocket server notified for token reset', [
                    'token' => substr($token, 0, 20) . '...',
                    'disconnected' => $result['data']['disconnected'] ?? 0,
                ]);
            } else {
                Log::warning('WebSocket server returned error for token reset', [
                    'token' => substr($token, 0, 20) . '...',
                    'status' => $response->status(),
                    'body' => $response->body(),
                ]);
            }
        } catch (\Exception $e) {
            // Log error but don't fail the reset operation
            Log::warning('Failed to notify WebSocket server for token reset', [
                'token' => substr($token, 0, 20) . '...',
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * Generate a unique token.
     *
     * @return string
     */
    protected function generateUniqueToken(): string
    {
        do {
            $token = Str::random(64);
        } while (PartnerToken::where('token', $token)->exists());

        return $token;
    }
}
