<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\Logger;
use App\Models\IntegrationModel;

/**
 * GoogleOAuthService
 *
 * Standard OAuth 2.0 authorization-code flow, used for both "Google
 * OAuth" (sign-in / general account linking) and "Google Calendar"
 * (a wider scope on the same flow) per the brief — one service, the
 * caller picks the scope. buildAuthorizationUrl() is pure string
 * building and fully testable; exchangeCodeForTokens()/
 * refreshAccessToken() need a live network round-trip against Google,
 * same graceful-failure pattern as every other external client this
 * phase.
 */
final class GoogleOAuthService
{
    private const AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';
    private const TOKEN_URL = 'https://oauth2.googleapis.com/token';

    public const SCOPE_SIGNIN = 'openid email profile';
    public const SCOPE_CALENDAR = 'openid email profile https://www.googleapis.com/auth/calendar.events';

    public function __construct(private readonly IntegrationModel $integrations = new IntegrationModel())
    {
    }

    public function buildAuthorizationUrl(string $slug, string $redirectUri, string $scope, string $state): ?string
    {
        $creds = $this->integrations->credentialsFor($slug);
        if ($creds === null || empty($creds['client_id'])) {
            return null;
        }

        $params = [
            'client_id' => $creds['client_id'],
            'redirect_uri' => $redirectUri,
            'response_type' => 'code',
            'scope' => $scope,
            'access_type' => 'offline',
            'prompt' => 'consent',
            'state' => $state,
        ];

        return self::AUTH_URL . '?' . http_build_query($params);
    }

    /** @return array{success:bool, access_token?:string, refresh_token?:string, expires_at?:string, error?:string} */
    public function exchangeCodeForTokens(string $slug, string $code, string $redirectUri): array
    {
        $creds = $this->integrations->credentialsFor($slug);
        if ($creds === null || empty($creds['client_id']) || empty($creds['client_secret'])) {
            return ['success' => false, 'error' => 'Google client ID/secret are not configured for this integration.'];
        }

        return $this->postToken([
            'client_id' => $creds['client_id'],
            'client_secret' => $creds['client_secret'],
            'code' => $code,
            'grant_type' => 'authorization_code',
            'redirect_uri' => $redirectUri,
        ]);
    }

    /** @return array{success:bool, access_token?:string, expires_at?:string, error?:string} */
    public function refreshAccessToken(string $slug, string $refreshToken): array
    {
        $creds = $this->integrations->credentialsFor($slug);
        if ($creds === null || empty($creds['client_id']) || empty($creds['client_secret'])) {
            return ['success' => false, 'error' => 'Google client ID/secret are not configured for this integration.'];
        }

        return $this->postToken([
            'client_id' => $creds['client_id'],
            'client_secret' => $creds['client_secret'],
            'refresh_token' => $refreshToken,
            'grant_type' => 'refresh_token',
        ]);
    }

    private function postToken(array $formFields): array
    {
        if (!function_exists('curl_init')) {
            return ['success' => false, 'error' => 'Server is missing the PHP curl extension.'];
        }

        $ch = curl_init(self::TOKEN_URL);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => http_build_query($formFields),
            CURLOPT_TIMEOUT => 20,
        ]);

        $body = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        if ($body === false || $error !== '') {
            Logger::error('GoogleOAuthService: request failed', ['error' => $error]);

            return ['success' => false, 'error' => "Could not reach Google: {$error}"];
        }

        $decoded = json_decode((string) $body, true);

        if ($status !== 200 || !is_array($decoded) || !isset($decoded['access_token'])) {
            $apiError = $decoded['error_description'] ?? $decoded['error'] ?? "HTTP {$status}";
            Logger::error('GoogleOAuthService: token exchange failed', ['error' => $apiError]);

            return ['success' => false, 'error' => $apiError];
        }

        return [
            'success' => true,
            'access_token' => $decoded['access_token'],
            'refresh_token' => $decoded['refresh_token'] ?? null,
            'expires_at' => date('Y-m-d H:i:s', time() + (int) ($decoded['expires_in'] ?? 3600)),
        ];
    }
}
