<?php

declare(strict_types=1);

namespace App\Domain\PreSave\Services;

use App\Domain\PreSave\PreSaveCampaign;
use App\Domain\PreSave\PreSaveCampaignRepositoryInterface;
use App\Domain\PreSave\PreSaveRegistration;
use App\Domain\PreSave\PreSaveRegistrationRepositoryInterface;
use App\Domain\PreSave\ValueObjects\StreamingPlatform;
use App\Framework\Logging\Logger;
use App\Framework\Logging\ValueObjects\LogContext;
use App\Framework\OAuth\OAuthServiceInterface;
use App\Framework\OAuth\Providers\SupportsPreSaves;
use App\Framework\OAuth\ValueObjects\OAuthToken;

/**
 * Pre-Save Processor
 *
 * Processes pre-save registrations on release day
 */
final readonly class PreSaveProcessor
{
    public function __construct(
        private PreSaveCampaignRepositoryInterface $campaignRepository,
        private PreSaveRegistrationRepositoryInterface $registrationRepository,
        private OAuthServiceInterface $oauthService,
        private SupportsPreSaves $musicProvider,
        private Logger $logger,
    ) {
    }

    /**
     * Process all campaigns ready for release
     *
     * @return array<string, mixed>
     */
    public function processReleasedCampaigns(): array
    {
        // Find campaigns that should be released
        $campaigns = $this->campaignRepository->findReadyForRelease();

        $results = [];

        foreach ($campaigns as $campaign) {
            // Mark campaign as released
            $releasedCampaign = $campaign->markAsReleased();
            $this->campaignRepository->save($releasedCampaign);

            $results[$campaign->id] = [
                'campaign_id' => $campaign->id,
                'title' => $campaign->title,
                'marked_as_released' => true,
            ];
        }

        return [
            'campaigns_marked_for_release' => count($campaigns),
            'results' => $results,
        ];
    }

    /**
     * Process all pending registrations for released campaigns
     *
     * @return array<string, mixed>
     */
    public function processPendingRegistrations(): array
    {
        $campaigns = $this->campaignRepository->findReadyForProcessing();

        $totalProcessed = 0;
        $totalSuccessful = 0;
        $totalFailed = 0;

        foreach ($campaigns as $campaign) {
            $result = $this->processCampaignRegistrations($campaign);

            $totalProcessed += $result['processed'];
            $totalSuccessful += $result['successful'];
            $totalFailed += $result['failed'];

            // Check if all registrations are processed
            $pending = $this->registrationRepository->findPendingByCampaign($campaign->id);

            if (empty($pending)) {
                // Mark campaign as completed
                $completedCampaign = $campaign->markAsCompleted();
                $this->campaignRepository->save($completedCampaign);
            }
        }

        return [
            'campaigns_processed' => count($campaigns),
            'total_processed' => $totalProcessed,
            'total_successful' => $totalSuccessful,
            'total_failed' => $totalFailed,
        ];
    }

    /**
     * Process registrations for a specific campaign
     *
     * @return array<string, int>
     */
    public function processCampaignRegistrations(PreSaveCampaign $campaign): array
    {
        $registrations = $this->registrationRepository->findPendingByCampaign($campaign->id);

        $processed = 0;
        $successful = 0;
        $failed = 0;

        foreach ($registrations as $registration) {
            try {
                $this->processRegistration($campaign, $registration);
                $successful++;
            } catch (\Exception $e) {
                $this->logger->error('Failed to process pre-save registration', LogContext::withData([
                    'registration_id' => $registration->id,
                    'campaign_id' => $campaign->id,
                    'user_id' => $registration->userId,
                    'platform' => $registration->platform->value,
                    'error' => $e->getMessage(),
                ]));
                $failed++;
            }

            $processed++;
        }

        return [
            'processed' => $processed,
            'successful' => $successful,
            'failed' => $failed,
        ];
    }

    /**
     * Process a single registration
     */
    private function processRegistration(
        PreSaveCampaign $campaign,
        PreSaveRegistration $registration
    ): void {
        // Get track URL for platform
        $trackUrl = $campaign->getTrackUrl($registration->platform);

        if ($trackUrl === null) {
            $failedRegistration = $registration->markAsFailed(
                'Track URL not found for platform'
            );
            $this->registrationRepository->save($failedRegistration);

            return;
        }

        try {
            // Get OAuth token for user (auto-refreshes if expired)
            $storedToken = $this->oauthService->getTokenForUser(
                $registration->userId,
                $registration->platform->getOAuthProvider()
            );

            // Process based on platform
            match ($registration->platform) {
                StreamingPlatform::SPOTIFY => $this->processMusicPlatformRegistration(
                    $storedToken->token,
                    $trackUrl->trackId,
                    $registration
                ),
                default => throw new \RuntimeException(
                    'Platform not yet supported: ' . $registration->platform->value
                ),
            };
        } catch (\Exception $e) {
            $failedRegistration = $registration->markAsFailed($e->getMessage());
            $this->registrationRepository->save($failedRegistration);

            throw $e;
        }
    }

    /**
     * Process music platform registration (Spotify, Apple Music, etc.)
     */
    private function processMusicPlatformRegistration(
        OAuthToken $token,
        string $trackId,
        PreSaveRegistration $registration
    ): void {
        // Add track to user's library
        $this->musicProvider->addTracksToLibrary($token, [$trackId]);

        // Mark registration as completed
        $completedRegistration = $registration->markAsCompleted();
        $this->registrationRepository->save($completedRegistration);

        $this->logger->info('Pre-save registration processed successfully', LogContext::withData([
            'registration_id' => $registration->id,
            'user_id' => $registration->userId,
            'platform' => $registration->platform->value,
            'track_id' => $trackId,
        ]));
    }

    /**
     * Retry failed registrations
     *
     * @return array<string, int>
     */
    public function retryFailedRegistrations(int $campaignId, int $maxRetries = 3): array
    {
        $campaign = $this->campaignRepository->findById($campaignId);

        if ($campaign === null) {
            throw new \InvalidArgumentException('Campaign not found');
        }

        $retryable = $this->registrationRepository->findRetryable($campaignId, $maxRetries);

        $processed = 0;
        $successful = 0;

        foreach ($retryable as $registration) {
            $resetRegistration = $registration->resetForRetry();
            $this->registrationRepository->save($resetRegistration);

            try {
                $this->processRegistration($campaign, $resetRegistration);
                $successful++;
            } catch (\Exception $e) {
                // Already logged in processRegistration
            }

            $processed++;
        }

        return [
            'processed' => $processed,
            'successful' => $successful,
            'failed' => $processed - $successful,
        ];
    }
}
