<?php

declare(strict_types=1);

namespace App\Application\Api;

use App\Domain\PreSave\PreSaveCampaignRepository;
use App\Domain\PreSave\PreSaveRegistration;
use App\Domain\PreSave\PreSaveRegistrationRepository;
use App\Domain\PreSave\ValueObjects\StreamingPlatform;
use App\Framework\Attributes\Route;
use App\Framework\Core\Method;
use App\Framework\Exception\ErrorCode;
use App\Framework\Exception\FrameworkException;
use App\Framework\Http\HttpRequest;
use App\Framework\Http\JsonResult;
use App\Framework\OAuth\Storage\OAuthTokenRepository;

/**
 * Pre-Save Campaign API Controller
 */
final readonly class PreSaveApiController
{
    public function __construct(
        private PreSaveCampaignRepository $campaignRepository,
        private PreSaveRegistrationRepository $registrationRepository,
        private OAuthTokenRepository $tokenRepository
    ) {
    }

    /**
     * Get active campaigns
     */
    #[Route(path: '/api/presave/campaigns', method: Method::GET)]
    public function getCampaigns(HttpRequest $request): JsonResult
    {
        try {
            $campaigns = $this->campaignRepository->findAll(['status' => 'active']);

            return new JsonResult([
                'success' => true,
                'data' => array_map(fn ($campaign) => [
                    'id' => $campaign->id,
                    'title' => $campaign->title,
                    'artist_name' => $campaign->artistName,
                    'cover_image_url' => $campaign->coverImageUrl,
                    'description' => $campaign->description,
                    'release_date' => $campaign->releaseDate->toTimestamp(),
                    'status' => $campaign->status->value,
                    'platforms' => array_map(fn ($url) => $url->platform->value, $campaign->trackUrls),
                ], $campaigns),
            ]);
        } catch (\Throwable $e) {
            throw FrameworkException::create(
                ErrorCode::DB_QUERY_FAILED,
                'Failed to fetch campaigns'
            )->withContext(
                $e->getMessage()
            );
        }
    }

    /**
     * Get campaign by ID
     */
    #[Route(path: '/api/presave/campaigns/{id}', method: Method::GET)]
    public function getCampaign(HttpRequest $request): JsonResult
    {
        $campaignId = $request->routeParams->get('id');

        // Validate ID is numeric
        if (! is_numeric($campaignId)) {
            throw FrameworkException::create(
                ErrorCode::VAL_INVALID_VALUE,
                'Campaign ID must be numeric'
            )->withData(['provided' => $campaignId]);
        }

        try {
            $campaign = $this->campaignRepository->findById((int) $campaignId);

            if ($campaign === null) {
                return new JsonResult([
                    'success' => false,
                    'message' => 'Campaign not found',
                ], 404);
            }

            return new JsonResult([
                'success' => true,
                'data' => [
                    'id' => $campaign->id,
                    'title' => $campaign->title,
                    'artist_name' => $campaign->artistName,
                    'cover_image_url' => $campaign->coverImageUrl,
                    'description' => $campaign->description,
                    'release_date' => $campaign->releaseDate->toTimestamp(),
                    'status' => $campaign->status->value,
                    'platforms' => array_map(fn ($url) => $url->platform->value, $campaign->trackUrls),
                    'track_urls' => array_map(fn ($url) => $url->toArray(), $campaign->trackUrls),
                ],
            ]);
        } catch (\Throwable $e) {
            throw FrameworkException::create(
                ErrorCode::DB_QUERY_FAILED,
                'Failed to fetch campaign'
            )->withContext($e->getMessage());
        }
    }

    /**
     * Register for pre-save campaign
     */
    #[Route(path: '/api/presave/campaigns/{id}/register', method: Method::POST)]
    public function register(HttpRequest $request): JsonResult
    {
        $campaignId = $request->routeParams->get('id');

        // Validate ID is numeric
        if (! is_numeric($campaignId)) {
            throw FrameworkException::create(
                ErrorCode::VAL_INVALID_VALUE,
                'Campaign ID must be numeric'
            )->withData(['provided' => $campaignId]);
        }

        $data = $request->parsedBody->toArray();

        // Validate campaign exists
        $campaign = $this->campaignRepository->findById((int) $campaignId);
        if ($campaign === null) {
            return new JsonResult([
                'success' => false,
                'message' => 'Campaign not found',
            ], 404);
        }

        // Validate required fields
        if (empty($data['user_id'])) {
            throw FrameworkException::create(
                ErrorCode::VAL_REQUIRED_FIELD_MISSING,
                'user_id is required'
            );
        }

        if (empty($data['platform'])) {
            throw FrameworkException::create(
                ErrorCode::VAL_REQUIRED_FIELD_MISSING,
                'platform is required'
            );
        }

        // Validate platform
        $platform = StreamingPlatform::tryFrom(strtolower($data['platform']));
        if ($platform === null) {
            throw FrameworkException::create(
                ErrorCode::VAL_INVALID_VALUE,
                "Invalid platform: {$data['platform']}"
            )->withData([
                'provided' => $data['platform'],
                'allowed' => ['spotify', 'apple_music', 'tidal'],
            ]);
        }

        // Check if user has OAuth token for this platform
        $platformName = match ($platform) {
            StreamingPlatform::SPOTIFY => 'spotify',
            StreamingPlatform::APPLE_MUSIC => 'apple_music',
            StreamingPlatform::TIDAL => 'tidal',
            default => $platform->value
        };

        $token = $this->tokenRepository->findForUser($data['user_id'], $platformName);
        if ($token === null) {
            return new JsonResult([
                'success' => false,
                'message' => "User not connected to {$platformName}",
                'oauth_required' => true,
                'oauth_url' => "/oauth/{$platformName}/authorize?user_id={$data['user_id']}&redirect_url=/presave/{$campaignId}",
            ], 401);
        }

        // Check for duplicate registration
        $existingRegistration = $this->registrationRepository->findForUserAndCampaign(
            $data['user_id'],
            $campaignId,
            $platform
        );

        if ($existingRegistration !== null) {
            return new JsonResult([
                'success' => false,
                'message' => 'You have already registered for this campaign',
                'registration' => [
                    'id' => $existingRegistration->id,
                    'status' => $existingRegistration->status->value,
                    'platform' => $existingRegistration->platform->value,
                    'registered_at' => $existingRegistration->registeredAt->toTimestamp(),
                ],
            ], 409);
        }

        // Create registration
        $registration = PreSaveRegistration::create(
            campaignId: (int) $campaignId,
            userId: $data['user_id'],
            platform: $platform
        );

        $savedRegistration = $this->registrationRepository->save($registration);

        return new JsonResult([
            'success' => true,
            'message' => 'Successfully registered for pre-save campaign',
            'data' => [
                'registration_id' => $savedRegistration->id,
                'campaign_id' => $campaignId,
                'platform' => $platform->value,
                'status' => $savedRegistration->status->value,
                'registered_at' => $savedRegistration->registeredAt->toTimestamp(),
            ],
        ], 201);
    }

    /**
     * Check user's registration status for a campaign
     */
    #[Route(path: '/api/presave/campaigns/{id}/status', method: Method::GET)]
    public function getRegistrationStatus(HttpRequest $request): JsonResult
    {
        $campaignId = $request->routeParams->get('id');
        $userId = $request->query->get('user_id');

        if ($userId === null) {
            throw FrameworkException::create(
                ErrorCode::VAL_REQUIRED_FIELD_MISSING,
                'user_id query parameter is required'
            );
        }

        // Get all registrations for this user and campaign
        $registrations = $this->registrationRepository->findByCampaign($campaignId);
        $userRegistrations = array_filter(
            $registrations,
            fn ($reg) => $reg->userId === $userId
        );

        if (empty($userRegistrations)) {
            return new JsonResult([
                'success' => true,
                'registered' => false,
                'registrations' => [],
            ]);
        }

        return new JsonResult([
            'success' => true,
            'registered' => true,
            'registrations' => array_map(fn ($reg) => [
                'id' => $reg->id,
                'platform' => $reg->platform->value,
                'status' => $reg->status->value,
                'registered_at' => $reg->registeredAt->toTimestamp(),
                'processed_at' => $reg->processedAt?->toTimestamp(),
                'error_message' => $reg->errorMessage,
            ], array_values($userRegistrations)),
        ]);
    }

    /**
     * Get user's OAuth connection status
     */
    #[Route(path: '/api/presave/oauth-status', method: Method::GET)]
    public function getOAuthStatus(HttpRequest $request): JsonResult
    {
        $userId = $request->query->get('user_id');

        if ($userId === null) {
            throw FrameworkException::create(
                ErrorCode::VAL_REQUIRED_FIELD_MISSING,
                'user_id query parameter is required'
            );
        }

        $connections = [];
        $platforms = ['spotify', 'apple_music', 'tidal'];

        foreach ($platforms as $platform) {
            $token = $this->tokenRepository->findForUser($userId, $platform);
            $connections[$platform] = [
                'connected' => $token !== null,
                'expires_at' => $token?->expiresAt,
                'needs_refresh' => $token !== null && $token->expiresAt < time(),
            ];
        }

        return new JsonResult([
            'success' => true,
            'user_id' => $userId,
            'connections' => $connections,
        ]);
    }

    /**
     * Get campaign statistics
     */
    #[Route(path: '/api/presave/campaigns/{id}/stats', method: Method::GET)]
    public function getStats(HttpRequest $request): JsonResult
    {
        $campaignId = $request->routeParams->get('id');
        $campaign = $this->campaignRepository->findById($campaignId);

        if ($campaign === null) {
            return new JsonResult([
                'success' => false,
                'message' => 'Campaign not found',
            ], 404);
        }

        $registrations = $this->registrationRepository->findByCampaign($campaignId);

        $stats = [
            'total_registrations' => count($registrations),
            'by_status' => [
                'pending' => 0,
                'processing' => 0,
                'completed' => 0,
                'failed' => 0,
            ],
            'by_platform' => [
                'spotify' => 0,
                'apple_music' => 0,
                'tidal' => 0,
            ],
        ];

        foreach ($registrations as $registration) {
            $stats['by_status'][strtolower($registration->status->value)]++;
            $stats['by_platform'][strtolower($registration->platform->value)]++;
        }

        return new JsonResult([
            'success' => true,
            'campaign_id' => $campaignId,
            'stats' => $stats,
        ]);
    }
}
