<?php

namespace App\Modules\Office365\Services;

use App\Modules\Office365\Models\Office365Token;
use App\Models\Setting;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;

class Office365AuthService
{
    protected $clientId;
    protected $clientSecret;
    protected $redirectUri;
    protected $tenantId;
    protected $scopes;

    public function __construct()
    {
        $this->clientId = Setting::get('integrations.office365.client_id', '');
        $this->clientSecret = Setting::get('integrations.office365.client_secret', '');
        $this->tenantId = Setting::get('integrations.office365.tenant_id', 'common');
        $this->redirectUri = Setting::get('integrations.office365.redirect_uri', url('/admin/settings/office365/callback'));
        $this->scopes = 'offline_access openid profile Calendars.Read';
    }

    /**
     * Get the authorization URL for OAuth flow
     */
    public function getAuthorizationUrl($state = null)
    {
        $state = $state ?: bin2hex(random_bytes(16));
        
        $params = [
            'client_id' => $this->clientId,
            'response_type' => 'code',
            'redirect_uri' => $this->redirectUri,
            'scope' => $this->scopes,
            'response_mode' => 'query',
            'state' => $state
        ];

        return "https://login.microsoftonline.com/{$this->tenantId}/oauth2/v2.0/authorize?" . http_build_query($params);
    }

    /**
     * Exchange authorization code for access token
     */
    public function exchangeCodeForToken($code, $userId = null)
    {
        $userId = $userId ?: Auth::id();
        
        try {
            Log::debug('Office365 token exchange request', [
                'client_id' => $this->clientId,
                'client_secret_length' => strlen($this->clientSecret),
                'tenant_id' => $this->tenantId,
                'redirect_uri' => $this->redirectUri,
                'user_id' => $userId
            ]);

            $response = Http::asForm()->post("https://login.microsoftonline.com/{$this->tenantId}/oauth2/v2.0/token", [
                'client_id' => $this->clientId,
                'client_secret' => $this->clientSecret,
                'code' => $code,
                'redirect_uri' => $this->redirectUri,
                'grant_type' => 'authorization_code',
            ]);

            if ($response->failed()) {
                Log::error('Office365 token exchange failed', [
                    'response' => $response->body(),
                    'user_id' => $userId
                ]);
                return false;
            }

            $data = $response->json();

            // Store or update token
            Office365Token::updateOrCreate(
                ['user_id' => $userId],
                [
                    'access_token' => $data['access_token'],
                    'refresh_token' => $data['refresh_token'],
                    'expires_at' => Carbon::now()->addSeconds($data['expires_in']),
                    'scope' => $data['scope'] ?? $this->scopes,
                    'token_type' => $data['token_type'] ?? 'Bearer'
                ]
            );

            Log::info('Office365 token stored successfully', ['user_id' => $userId]);
            return true;

        } catch (\Exception $e) {
            Log::error('Office365 token exchange error', [
                'error' => $e->getMessage(),
                'user_id' => $userId
            ]);
            return false;
        }
    }

    /**
     * Refresh an expired access token
     */
    public function refreshToken(Office365Token $token)
    {
        try {
            $response = Http::asForm()->post("https://login.microsoftonline.com/{$this->tenantId}/oauth2/v2.0/token", [
                'client_id' => $this->clientId,
                'client_secret' => $this->clientSecret,
                'refresh_token' => $token->refresh_token,
                'grant_type' => 'refresh_token',
            ]);

            if ($response->failed()) {
                Log::error('Office365 token refresh failed', [
                    'response' => $response->body(),
                    'user_id' => $token->user_id
                ]);
                return false;
            }

            $data = $response->json();

            $token->update([
                'access_token' => $data['access_token'],
                'refresh_token' => $data['refresh_token'] ?? $token->refresh_token,
                'expires_at' => Carbon::now()->addSeconds($data['expires_in']),
            ]);

            return true;

        } catch (\Exception $e) {
            Log::error('Office365 token refresh error', [
                'error' => $e->getMessage(),
                'user_id' => $token->user_id
            ]);
            return false;
        }
    }

    /**
     * Get a valid access token for a user
     */
    public function getValidToken($userId = null)
    {
        $userId = $userId ?: Auth::id();
        $token = Office365Token::forUser($userId);

        if (!$token) {
            return null;
        }

        // If token is expired or will expire soon, refresh it
        if ($token->isExpired() || $token->willExpireSoon()) {
            if (!$this->refreshToken($token)) {
                // Refresh failed, token is invalid
                return null;
            }
            // Reload the token after refresh
            $token->refresh();
        }

        return $token->access_token;
    }

    /**
     * Check if user has valid authentication
     */
    public function isAuthenticated($userId = null)
    {
        return $this->getValidToken($userId) !== null;
    }

    /**
     * Revoke user's authentication
     */
    public function revokeAccess($userId = null)
    {
        $userId = $userId ?: Auth::id();
        
        try {
            Office365Token::where('user_id', $userId)->delete();
            Log::info('Office365 access revoked', ['user_id' => $userId]);
            return true;
        } catch (\Exception $e) {
            Log::error('Failed to revoke Office365 access', [
                'error' => $e->getMessage(),
                'user_id' => $userId
            ]);
            return false;
        }
    }
}