<?php

namespace App\Services;

use App\Models\StravaConnection;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Http;
use RuntimeException;

class StravaClient
{
    private const AUTHORIZE_URL = 'https://www.strava.com/oauth/mobile/authorize';

    public function authorizationUrl(string $state): string
    {
        $clientId = config('services.strava.client_id');

        if (! $clientId) {
            throw new RuntimeException('Strava is not configured.');
        }

        return self::AUTHORIZE_URL.'?'.http_build_query([
            'client_id' => $clientId,
            'redirect_uri' => $this->redirectUri(),
            'response_type' => 'code',
            'approval_prompt' => 'auto',
            'scope' => config('services.strava.scope', 'read,activity:read'),
            'state' => $state,
        ], '', '&', PHP_QUERY_RFC3986);
    }

    public function exchangeCode(string $code): array
    {
        return $this->tokenRequest()
            ->post('/oauth/token', [
                'client_id' => config('services.strava.client_id'),
                'client_secret' => config('services.strava.client_secret'),
                'code' => $code,
                'grant_type' => 'authorization_code',
            ])
            ->throw()
            ->json();
    }

    public function refresh(StravaConnection $connection): StravaConnection
    {
        $payload = $this->tokenRequest()
            ->post('/oauth/token', [
                'client_id' => config('services.strava.client_id'),
                'client_secret' => config('services.strava.client_secret'),
                'grant_type' => 'refresh_token',
                'refresh_token' => $connection->refresh_token,
            ])
            ->throw()
            ->json();

        $connection->update([
            'access_token' => $payload['access_token'],
            'refresh_token' => $payload['refresh_token'],
            'expires_at' => Carbon::createFromTimestamp((int) $payload['expires_at']),
        ]);

        return $connection->refresh();
    }

    public function deauthorize(StravaConnection $connection): void
    {
        $accessToken = $this->accessToken($connection);

        Http::baseUrl('https://www.strava.com')
            ->timeout(10)
            ->connectTimeout(3)
            ->retry([100, 500, 1000])
            ->post('/oauth/deauthorize?'.http_build_query([
                'access_token' => $accessToken,
            ], '', '&', PHP_QUERY_RFC3986))
            ->throw();
    }

    public function activities(StravaConnection $connection, int $page, int $perPage): array
    {
        return Http::baseUrl('https://www.strava.com/api/v3')
            ->withToken($this->accessToken($connection))
            ->timeout(10)
            ->connectTimeout(3)
            ->retry([100, 500, 1000])
            ->get('/athlete/activities', [
                'page' => $page,
                'per_page' => $perPage,
            ])
            ->throw()
            ->json();
    }

    public function activity(StravaConnection $connection, int|string $activityId): array
    {
        return Http::baseUrl('https://www.strava.com/api/v3')
            ->withToken($this->accessToken($connection))
            ->timeout(10)
            ->connectTimeout(3)
            ->retry([100, 500, 1000])
            ->get("/activities/{$activityId}", [
                'include_all_efforts' => false,
            ])
            ->throw()
            ->json();
    }

    private function accessToken(StravaConnection $connection): string
    {
        if (! $connection->expires_at || $connection->expires_at->lte(now()->addMinutes(5))) {
            $connection = $this->refresh($connection);
        }

        return $connection->access_token;
    }

    private function tokenRequest(): PendingRequest
    {
        return Http::baseUrl('https://www.strava.com')
            ->asForm()
            ->timeout(10)
            ->connectTimeout(3)
            ->retry([100, 500, 1000]);
    }

    private function redirectUri(): string
    {
        return rtrim((string) config('app.url'), '/').'/strava/callback';
    }
}
