<?php

namespace App\Services;

use App\Models\ProviderCredential;
use App\Models\Brand;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use Exception;

class OAuthService
{
    /**
     * Generate OAuth authorization URL for a provider.
     */
    public function getAuthorizationUrl(string $provider, Brand $brand, array $scopes = []): string
    {
        $state = base64_encode(json_encode([
            'brand_id' => $brand->id,
            'provider' => $provider,
            'timestamp' => now()->timestamp,
            'nonce' => Str::random(32),
        ]));

        session(['oauth_state' => $state]);

        return match($provider) {
            'youtube', 'google_ads' => $this->getGoogleAuthUrl($scopes, $state),
            'instagram', 'facebook_ads' => $this->getFacebookAuthUrl($scopes, $state),
            'tiktok' => $this->getTikTokAuthUrl($scopes, $state),
            'linkedin' => $this->getLinkedInAuthUrl($scopes, $state),
            'twitter' => $this->getTwitterAuthUrl($scopes, $state),
            default => throw new Exception("OAuth not supported for provider: {$provider}"),
        };
    }

    /**
     * Handle OAuth callback and exchange code for tokens.
     */
    public function handleCallback(string $provider, string $code, string $state): array
    {
        // Verify state
        if ($state !== session('oauth_state')) {
            throw new Exception('Invalid OAuth state');
        }

        session()->forget('oauth_state');

        $stateData = json_decode(base64_decode($state), true);

        return match($provider) {
            'youtube', 'google_ads' => $this->handleGoogleCallback($code, $stateData),
            'instagram', 'facebook_ads' => $this->handleFacebookCallback($code, $stateData),
            'tiktok' => $this->handleTikTokCallback($code, $stateData),
            'linkedin' => $this->handleLinkedInCallback($code, $stateData),
            'twitter' => $this->handleTwitterCallback($code, $stateData),
            default => throw new Exception("OAuth callback not supported for provider: {$provider}"),
        };
    }

    /**
     * Get Google OAuth authorization URL.
     */
    protected function getGoogleAuthUrl(array $scopes, string $state): string
    {
        $defaultScopes = [
            'https://www.googleapis.com/auth/youtube.readonly',
            'https://www.googleapis.com/auth/adwords',
        ];

        $scopes = !empty($scopes) ? $scopes : $defaultScopes;

        $params = http_build_query([
            'client_id' => config('services.google.client_id'),
            'redirect_uri' => route('brand.integrations.oauth.callback', ['provider' => 'google']),
            'response_type' => 'code',
            'scope' => implode(' ', $scopes),
            'access_type' => 'offline',
            'prompt' => 'consent',
            'state' => $state,
        ]);

        return "https://accounts.google.com/o/oauth2/v2/auth?{$params}";
    }

    /**
     * Handle Google OAuth callback.
     */
    protected function handleGoogleCallback(string $code, array $stateData): array
    {
        $response = Http::asForm()->post('https://oauth2.googleapis.com/token', [
            'client_id' => config('services.google.client_id'),
            'client_secret' => config('services.google.client_secret'),
            'code' => $code,
            'grant_type' => 'authorization_code',
            'redirect_uri' => route('brand.integrations.oauth.callback', ['provider' => 'google']),
        ]);

        if (!$response->successful()) {
            throw new Exception('Failed to exchange code for tokens: ' . $response->body());
        }

        $data = $response->json();

        return [
            'access_token' => $data['access_token'],
            'refresh_token' => $data['refresh_token'] ?? null,
            'expires_in' => $data['expires_in'] ?? 3600,
            'token_type' => $data['token_type'] ?? 'Bearer',
            'scope' => $data['scope'] ?? null,
        ];
    }

    /**
     * Get Facebook OAuth authorization URL.
     */
    protected function getFacebookAuthUrl(array $scopes, string $state): string
    {
        $defaultScopes = [
            'instagram_basic',
            'instagram_content_publish',
            'pages_show_list',
            'ads_management',
            'ads_read',
        ];

        $scopes = !empty($scopes) ? $scopes : $defaultScopes;

        $params = http_build_query([
            'client_id' => config('services.facebook.client_id'),
            'redirect_uri' => route('brand.integrations.oauth.callback', ['provider' => 'facebook']),
            'scope' => implode(',', $scopes),
            'state' => $state,
        ]);

        return "https://www.facebook.com/v18.0/dialog/oauth?{$params}";
    }

    /**
     * Handle Facebook OAuth callback.
     */
    protected function handleFacebookCallback(string $code, array $stateData): array
    {
        $response = Http::get('https://graph.facebook.com/v18.0/oauth/access_token', [
            'client_id' => config('services.facebook.client_id'),
            'client_secret' => config('services.facebook.client_secret'),
            'code' => $code,
            'redirect_uri' => route('brand.integrations.oauth.callback', ['provider' => 'facebook']),
        ]);

        if (!$response->successful()) {
            throw new Exception('Failed to exchange code for tokens: ' . $response->body());
        }

        $data = $response->json();

        // Exchange for long-lived token
        $longLivedResponse = Http::get('https://graph.facebook.com/v18.0/oauth/access_token', [
            'grant_type' => 'fb_exchange_token',
            'client_id' => config('services.facebook.client_id'),
            'client_secret' => config('services.facebook.client_secret'),
            'fb_exchange_token' => $data['access_token'],
        ]);

        if ($longLivedResponse->successful()) {
            $longLivedData = $longLivedResponse->json();
            $data['access_token'] = $longLivedData['access_token'];
            $data['expires_in'] = $longLivedData['expires_in'] ?? 5184000; // 60 days
        }

        return [
            'access_token' => $data['access_token'],
            'refresh_token' => null, // Facebook doesn't provide refresh tokens
            'expires_in' => $data['expires_in'] ?? 5184000,
            'token_type' => $data['token_type'] ?? 'Bearer',
        ];
    }

    /**
     * Get TikTok OAuth authorization URL.
     */
    protected function getTikTokAuthUrl(array $scopes, string $state): string
    {
        $defaultScopes = ['user.info.basic', 'video.list'];
        $scopes = !empty($scopes) ? $scopes : $defaultScopes;

        $params = http_build_query([
            'client_key' => config('services.tiktok.client_key'),
            'scope' => implode(',', $scopes),
            'response_type' => 'code',
            'redirect_uri' => route('brand.integrations.oauth.callback', ['provider' => 'tiktok']),
            'state' => $state,
        ]);

        return "https://www.tiktok.com/auth/authorize/?{$params}";
    }

    /**
     * Handle TikTok OAuth callback.
     */
    protected function handleTikTokCallback(string $code, array $stateData): array
    {
        $response = Http::asForm()->post('https://open-api.tiktok.com/oauth/access_token/', [
            'client_key' => config('services.tiktok.client_key'),
            'client_secret' => config('services.tiktok.client_secret'),
            'code' => $code,
            'grant_type' => 'authorization_code',
        ]);

        if (!$response->successful()) {
            throw new Exception('Failed to exchange code for tokens: ' . $response->body());
        }

        $data = $response->json()['data'] ?? [];

        return [
            'access_token' => $data['access_token'],
            'refresh_token' => $data['refresh_token'] ?? null,
            'expires_in' => $data['expires_in'] ?? 86400,
            'token_type' => 'Bearer',
        ];
    }

    /**
     * Get LinkedIn OAuth authorization URL.
     */
    protected function getLinkedInAuthUrl(array $scopes, string $state): string
    {
        $defaultScopes = ['r_liteprofile', 'w_member_social'];
        $scopes = !empty($scopes) ? $scopes : $defaultScopes;

        $params = http_build_query([
            'response_type' => 'code',
            'client_id' => config('services.linkedin.client_id'),
            'redirect_uri' => route('brand.integrations.oauth.callback', ['provider' => 'linkedin']),
            'scope' => implode(' ', $scopes),
            'state' => $state,
        ]);

        return "https://www.linkedin.com/oauth/v2/authorization?{$params}";
    }

    /**
     * Handle LinkedIn OAuth callback.
     */
    protected function handleLinkedInCallback(string $code, array $stateData): array
    {
        $response = Http::asForm()->post('https://www.linkedin.com/oauth/v2/accessToken', [
            'grant_type' => 'authorization_code',
            'code' => $code,
            'client_id' => config('services.linkedin.client_id'),
            'client_secret' => config('services.linkedin.client_secret'),
            'redirect_uri' => route('brand.integrations.oauth.callback', ['provider' => 'linkedin']),
        ]);

        if (!$response->successful()) {
            throw new Exception('Failed to exchange code for tokens: ' . $response->body());
        }

        $data = $response->json();

        return [
            'access_token' => $data['access_token'],
            'refresh_token' => $data['refresh_token'] ?? null,
            'expires_in' => $data['expires_in'] ?? 5184000,
            'token_type' => $data['token_type'] ?? 'Bearer',
        ];
    }

    /**
     * Get Twitter OAuth authorization URL.
     */
    protected function getTwitterAuthUrl(array $scopes, string $state): string
    {
        $defaultScopes = ['tweet.read', 'users.read', 'offline.access'];
        $scopes = !empty($scopes) ? $scopes : $defaultScopes;

        // Generate code verifier and challenge for PKCE
        $codeVerifier = Str::random(128);
        $codeChallenge = rtrim(strtr(base64_encode(hash('sha256', $codeVerifier, true)), '+/', '-_'), '=');

        session(['twitter_code_verifier' => $codeVerifier]);

        $params = http_build_query([
            'response_type' => 'code',
            'client_id' => config('services.twitter.client_id'),
            'redirect_uri' => route('brand.integrations.oauth.callback', ['provider' => 'twitter']),
            'scope' => implode(' ', $scopes),
            'state' => $state,
            'code_challenge' => $codeChallenge,
            'code_challenge_method' => 'S256',
        ]);

        return "https://twitter.com/i/oauth2/authorize?{$params}";
    }

    /**
     * Handle Twitter OAuth callback.
     */
    protected function handleTwitterCallback(string $code, array $stateData): array
    {
        $codeVerifier = session('twitter_code_verifier');
        session()->forget('twitter_code_verifier');

        $response = Http::asForm()
            ->withBasicAuth(
                config('services.twitter.client_id'),
                config('services.twitter.client_secret')
            )
            ->post('https://api.twitter.com/2/oauth2/token', [
                'code' => $code,
                'grant_type' => 'authorization_code',
                'client_id' => config('services.twitter.client_id'),
                'redirect_uri' => route('brand.integrations.oauth.callback', ['provider' => 'twitter']),
                'code_verifier' => $codeVerifier,
            ]);

        if (!$response->successful()) {
            throw new Exception('Failed to exchange code for tokens: ' . $response->body());
        }

        $data = $response->json();

        return [
            'access_token' => $data['access_token'],
            'refresh_token' => $data['refresh_token'] ?? null,
            'expires_in' => $data['expires_in'] ?? 7200,
            'token_type' => $data['token_type'] ?? 'Bearer',
        ];
    }
}