<?php

declare(strict_types=1);

namespace App\Application\Campaign;

use App\Application\Campaign\Services\SpotifyCampaignService;
use App\Framework\Attributes\Route;
use App\Framework\Http\Method;
use App\Framework\Http\Request;
use App\Framework\Http\Responses\Redirect;
use App\Framework\OAuth\Providers\SpotifyProvider;

/**
 * Spotify OAuth Callback Handler
 *
 * Handles OAuth callback from Spotify and adds tracks to user's library
 */
final readonly class SpotifyCallbackHandler
{
    public function __construct(
        private SpotifyProvider $spotifyProvider,
        private SpotifyCampaignService $spotifyCampaignService,
        private CampaignService $campaignService
    ) {
    }

    #[Route(path: '/campaign/spotify/callback', method: Method::GET)]
    public function __invoke(Request $request): Redirect
    {
        // Get OAuth code and state from callback
        $code = $request->query->get('code');
        $state = $request->query->get('state');
        $error = $request->query->get('error');

        // Handle OAuth errors
        if ($error) {
            return Redirect::to('/')
                ->withFlash('error', "Spotify authorization failed: {$error}");
        }

        // Validate state (CSRF protection)
        $expectedState = $request->session->get('spotify_oauth_state');
        if (! $state || $state !== $expectedState) {
            return Redirect::to('/')
                ->withFlash('error', 'Invalid OAuth state. Please try again.');
        }

        // Get campaign slug from session
        $campaignSlug = $request->session->get('spotify_campaign_slug');
        if (! $campaignSlug) {
            return Redirect::to('/')
                ->withFlash('error', 'Campaign session expired. Please try again.');
        }

        try {
            // Exchange code for access token
            $token = $this->spotifyProvider->getAccessToken($code, $state);

            // Get campaign data
            $campaign = $this->campaignService->findBySlug($campaignSlug);
            if (! $campaign) {
                return Redirect::to('/')
                    ->withFlash('error', 'Campaign not found');
            }

            // Add campaign tracks to user's Spotify library
            $success = $this->spotifyCampaignService->addCampaignToLibrary($token, $campaign);

            if (! $success) {
                return Redirect::to("/campaign/{$campaignSlug}")
                    ->withFlash('error', 'Failed to add tracks to your library');
            }

            // Get user profile for personalization
            $profile = $this->spotifyCampaignService->getUserProfile($token);

            // Record pre-save with email if available
            $this->campaignService->recordPreSave(
                campaignId: $campaign->id,
                platform: 'spotify',
                sessionId: $request->session->getId(),
                email: $profile['email'] ?? null
            );

            // Clean up session
            $request->session->forget('spotify_oauth_state');
            $request->session->forget('spotify_campaign_slug');

            // Redirect to success page
            return Redirect::to("/campaign/{$campaignSlug}/success")
                ->withFlash('success', "Successfully added {$campaign->album_title} to your Spotify library!")
                ->withSession([
                    'presave_platform' => 'Spotify',
                    'presave_url' => "https://open.spotify.com/album/" . $this->extractSpotifyId($campaign->spotify_uri),
                    'user_name' => $profile['display_name'] ?? null,
                ]);

        } catch (\Exception $e) {
            return Redirect::to("/campaign/{$campaignSlug}")
                ->withFlash('error', 'An error occurred during Spotify authorization');
        }
    }

    /**
     * Extract Spotify ID from URI
     */
    private function extractSpotifyId(?string $uri): ?string
    {
        if ($uri === null) {
            return null;
        }

        if (str_starts_with($uri, 'spotify:')) {
            $parts = explode(':', $uri);

            return $parts[2] ?? null;
        }

        return $uri;
    }
}
