<?php

declare(strict_types=1);

namespace App\Application\Admin;

use App\Domain\PreSave\PreSaveCampaign;
use App\Domain\PreSave\PreSaveCampaignRepository;
use App\Domain\PreSave\Services\PreSaveCampaignService;
use App\Domain\PreSave\ValueObjects\CampaignStatus;
use App\Domain\PreSave\ValueObjects\TrackUrl;
use App\Framework\Admin\AdminPageRenderer;
use App\Framework\Admin\Attributes\AdminResource;
use App\Framework\Admin\Factories\AdminFormFactory;
use App\Framework\Admin\Factories\AdminTableFactory;
use App\Framework\Admin\ValueObjects\AdminFormConfig;
use App\Framework\Admin\ValueObjects\AdminTableConfig;
use App\Framework\Attributes\Route;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Exception\ErrorCode;
use App\Framework\Exception\FrameworkException;
use App\Framework\Http\HttpRequest;
use App\Framework\Http\Method;
use App\Framework\Router\Result\JsonResult;
use App\Framework\Router\Result\Redirect;
use App\Framework\Router\Result\ViewResult;

/**
 * Pre-Save Campaign Admin Controller
 *
 * Admin interface for managing pre-save campaigns
 */
#[AdminResource(
    name: 'presave-campaigns',
    singularName: 'Pre-Save Campaign',
    pluralName: 'Pre-Save Campaigns',
    icon: 'music',
    enableApi: true,
    enableCrud: true
)]
final readonly class PreSaveCampaignAdminController
{
    public function __construct(
        private AdminPageRenderer $pageRenderer,
        private AdminTableFactory $tableFactory,
        private AdminFormFactory $formFactory,
        private PreSaveCampaignRepository $repository,
        private PreSaveCampaignService $service,
    ) {
    }

    /**
     * List all campaigns
     */
    #[Route('/admin/presave-campaigns', Method::GET)]
    public function index(): ViewResult
    {
        $tableConfig = AdminTableConfig::create(
            resource: 'presave-campaigns',
            columns: [
                'id' => [
                    'label' => 'ID',
                    'sortable' => true,
                    'class' => 'text-center',
                ],
                'title' => [
                    'label' => 'Title',
                    'sortable' => true,
                    'searchable' => true,
                ],
                'artist_name' => [
                    'label' => 'Artist',
                    'sortable' => true,
                    'searchable' => true,
                ],
                'release_date' => [
                    'label' => 'Release Date',
                    'sortable' => true,
                    'formatter' => 'date',
                ],
                'status' => [
                    'label' => 'Status',
                    'formatter' => 'status',
                ],
                'created_at' => [
                    'label' => 'Created',
                    'sortable' => true,
                    'formatter' => 'date',
                ],
            ],
            sortable: true,
            searchable: true
        );

        $campaigns = $this->repository->findAll();
        $campaignData = array_map(
            fn ($campaign) => [
                ...$campaign->toArray(),
                'release_date' => $campaign->releaseDate->format('Y-m-d H:i'),
                'created_at' => $campaign->createdAt->format('Y-m-d H:i'),
            ],
            $campaigns
        );

        $table = $this->tableFactory->create($tableConfig, $campaignData);

        return $this->pageRenderer->renderIndex(
            resource: 'presave-campaigns',
            table: $table,
            title: 'Pre-Save Campaigns',
            actions: [
                [
                    'url' => '/admin/presave-campaigns/create',
                    'label' => 'Create Campaign',
                    'icon' => 'plus',
                ],
            ]
        );
    }

    /**
     * Show campaign statistics
     */
    #[Route('/admin/presave-campaigns/{id}', Method::GET)]
    public function show(int $id): ViewResult
    {
        try {
            $campaign = $this->repository->findById($id);

            if ($campaign === null) {
                throw FrameworkException::create(
                    ErrorCode::ENTITY_NOT_FOUND,
                    'Campaign not found'
                )->withData(['campaign_id' => $id]);
            }

            $stats = $this->service->getCampaignStats($id);

            return $this->pageRenderer->renderShow(
                resource: 'presave-campaigns',
                title: $campaign->title,
                data: [
                    'campaign' => $campaign->toArray(),
                    'stats' => $stats,
                ]
            );
        } catch (FrameworkException $e) {
            throw $e;
        } catch (\Throwable $e) {
            throw FrameworkException::create(
                ErrorCode::DB_QUERY_FAILED,
                'Failed to load campaign'
            )->withContext($e->getMessage());
        }
    }

    /**
     * API endpoint for campaign list
     */
    #[Route('/admin/api/presave-campaigns', Method::GET)]
    public function apiList(HttpRequest $request): JsonResult
    {
        $campaigns = $this->repository->findAll();

        $items = array_map(
            fn ($campaign) => [
                ...$campaign->toArray(),
                'release_date' => $campaign->releaseDate->format('Y-m-d H:i'),
                'created_at' => $campaign->createdAt->format('Y-m-d H:i'),
            ],
            $campaigns
        );

        return new JsonResult([
            'success' => true,
            'data' => $items,
            'pagination' => [
                'page' => 1,
                'per_page' => count($items),
                'total' => count($items),
                'pages' => 1,
            ],
        ]);
    }

    /**
     * API endpoint for campaign statistics
     */
    #[Route('/admin/api/presave-campaigns/{id}/stats', Method::GET)]
    public function apiStats(int $id): JsonResult
    {
        try {
            $stats = $this->service->getCampaignStats($id);

            return new JsonResult([
                'success' => true,
                'data' => $stats,
            ]);
        } catch (\Exception $e) {
            return new JsonResult([
                'success' => false,
                'error' => $e->getMessage(),
            ], 404);
        }
    }

    /**
     * Show create campaign form
     */
    #[Route('/admin/presave-campaigns/create', Method::GET)]
    public function create(): ViewResult
    {
        $formConfig = AdminFormConfig::create(
            resource: 'presave-campaigns',
            action: '/admin/presave-campaigns',
            method: Method::POST,
            fields: [
                'title' => [
                    'type' => 'text',
                    'label' => 'Campaign Title',
                    'required' => true,
                    'placeholder' => 'e.g., New Album Release',
                    'help' => 'Name of the album or single',
                ],
                'artist_name' => [
                    'type' => 'text',
                    'label' => 'Artist Name',
                    'required' => true,
                    'placeholder' => 'e.g., The Artist',
                ],
                'cover_image_url' => [
                    'type' => 'text',
                    'label' => 'Cover Image URL',
                    'required' => true,
                    'placeholder' => 'https://...',
                    'help' => 'URL to album/single artwork',
                ],
                'description' => [
                    'type' => 'textarea',
                    'label' => 'Description',
                    'placeholder' => 'Tell fans about this release...',
                    'help' => 'Optional description shown on the pre-save page',
                ],
                'release_date' => [
                    'type' => 'text',
                    'label' => 'Release Date',
                    'required' => true,
                    'placeholder' => 'YYYY-MM-DD HH:MM',
                    'help' => 'When the track/album will be released',
                ],
                'start_date' => [
                    'type' => 'text',
                    'label' => 'Campaign Start Date',
                    'placeholder' => 'YYYY-MM-DD HH:MM (optional)',
                    'help' => 'When to start accepting pre-saves (optional, defaults to now)',
                ],
                'track_url_spotify' => [
                    'type' => 'text',
                    'label' => 'Spotify Track URL',
                    'placeholder' => 'https://open.spotify.com/track/...',
                    'help' => 'Spotify track link for pre-save',
                ],
                'track_url_tidal' => [
                    'type' => 'text',
                    'label' => 'Tidal Track URL (optional)',
                    'placeholder' => 'https://tidal.com/browse/track/...',
                ],
                'track_url_apple_music' => [
                    'type' => 'text',
                    'label' => 'Apple Music Track URL (optional)',
                    'placeholder' => 'https://music.apple.com/...',
                ],
            ]
        );

        $form = $this->formFactory->create($formConfig);

        return $this->pageRenderer->renderForm(
            resource: 'presave-campaigns',
            form: $form,
            title: 'Create Pre-Save Campaign',
            backUrl: '/admin/presave-campaigns'
        );
    }

    /**
     * Store new campaign
     */
    #[Route('/admin/presave-campaigns', Method::POST)]
    public function store(HttpRequest $request): Redirect
    {
        $data = $request->parsedBody->toArray();

        // Parse track URLs
        $trackUrls = [];
        if (! empty($data['track_url_spotify'])) {
            $trackUrls[] = TrackUrl::fromUrl($data['track_url_spotify']);
        }
        if (! empty($data['track_url_tidal'])) {
            $trackUrls[] = TrackUrl::fromUrl($data['track_url_tidal']);
        }
        if (! empty($data['track_url_apple_music'])) {
            $trackUrls[] = TrackUrl::fromUrl($data['track_url_apple_music']);
        }

        try {
            // Validate at least one track URL
            if (empty($trackUrls)) {
                throw FrameworkException::create(
                    ErrorCode::VAL_REQUIRED_FIELD_MISSING,
                    'At least one track URL is required'
                );
            }

            // Create campaign entity
            $campaign = PreSaveCampaign::create(
                title: $data['title'],
                artistName: $data['artist_name'],
                coverImageUrl: $data['cover_image_url'],
                releaseDate: Timestamp::fromDateTime(new \DateTimeImmutable($data['release_date'])),
                trackUrls: $trackUrls,
                description: ! empty($data['description']) ? $data['description'] : null,
                startDate: ! empty($data['start_date']) ? Timestamp::fromDateTime(new \DateTimeImmutable($data['start_date'])) : null
            );

            // Save campaign
            $savedCampaign = $this->repository->save($campaign);

            return new Redirect('/admin/presave-campaigns/' . $savedCampaign->id);
        } catch (FrameworkException $e) {
            throw $e;
        } catch (\Throwable $e) {
            throw FrameworkException::create(
                ErrorCode::DB_QUERY_FAILED,
                'Failed to create campaign'
            )->withContext($e->getMessage());
        }
    }

    /**
     * Show edit campaign form
     */
    #[Route('/admin/presave-campaigns/{id}/edit', Method::GET)]
    public function edit(int $id): ViewResult
    {
        $campaign = $this->repository->findById($id);

        if ($campaign === null) {
            throw FrameworkException::create(
                ErrorCode::ENTITY_NOT_FOUND,
                'Campaign not found'
            )->withData(['campaign_id' => $id]);
        }

        // Extract track URLs by platform
        $trackUrlData = [];
        foreach ($campaign->trackUrls as $trackUrl) {
            $trackUrlData['track_url_' . $trackUrl->platform->value] = $trackUrl->url;
        }

        $formConfig = AdminFormConfig::create(
            resource: 'presave-campaigns',
            action: '/admin/presave-campaigns/' . $id,
            method: Method::POST,
            fields: [
                '_method' => [
                    'type' => 'hidden',
                ],
                'title' => [
                    'type' => 'text',
                    'label' => 'Campaign Title',
                    'required' => true,
                ],
                'artist_name' => [
                    'type' => 'text',
                    'label' => 'Artist Name',
                    'required' => true,
                ],
                'cover_image_url' => [
                    'type' => 'text',
                    'label' => 'Cover Image URL',
                    'required' => true,
                ],
                'description' => [
                    'type' => 'textarea',
                    'label' => 'Description',
                ],
                'release_date' => [
                    'type' => 'text',
                    'label' => 'Release Date',
                    'required' => true,
                ],
                'start_date' => [
                    'type' => 'text',
                    'label' => 'Campaign Start Date',
                ],
                'track_url_spotify' => [
                    'type' => 'text',
                    'label' => 'Spotify Track URL',
                ],
                'track_url_tidal' => [
                    'type' => 'text',
                    'label' => 'Tidal Track URL',
                ],
                'track_url_apple_music' => [
                    'type' => 'text',
                    'label' => 'Apple Music Track URL',
                ],
                'status' => [
                    'type' => 'select',
                    'label' => 'Status',
                    'required' => true,
                    'options' => [
                        CampaignStatus::DRAFT->value => 'Draft',
                        CampaignStatus::SCHEDULED->value => 'Scheduled',
                        CampaignStatus::ACTIVE->value => 'Active',
                        CampaignStatus::CANCELLED->value => 'Cancelled',
                    ],
                    'help' => 'Campaign lifecycle status',
                ],
            ]
        )->withData([
            '_method' => 'PUT',
            'title' => $campaign->title,
            'artist_name' => $campaign->artistName,
            'cover_image_url' => $campaign->coverImageUrl,
            'description' => $campaign->description ?? '',
            'release_date' => $campaign->releaseDate->format('Y-m-d H:i'),
            'start_date' => $campaign->startDate?->format('Y-m-d H:i') ?? '',
            'status' => $campaign->status->value,
            ...$trackUrlData,
        ]);

        $form = $this->formFactory->create($formConfig);

        return $this->pageRenderer->renderForm(
            resource: 'presave-campaigns',
            form: $form,
            title: 'Edit Campaign: ' . $campaign->title,
            backUrl: '/admin/presave-campaigns'
        );
    }

    /**
     * Update existing campaign
     */
    #[Route('/admin/presave-campaigns/{id}', Method::POST)]
    public function update(int $id, HttpRequest $request): Redirect
    {
        try {
            $campaign = $this->repository->findById($id);

            if ($campaign === null) {
                throw FrameworkException::create(
                    ErrorCode::ENTITY_NOT_FOUND,
                    'Campaign not found'
                )->withData(['campaign_id' => $id]);
            }

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

            // Parse track URLs
            $trackUrls = [];
            if (! empty($data['track_url_spotify'])) {
                $trackUrls[] = TrackUrl::fromUrl($data['track_url_spotify']);
            }
            if (! empty($data['track_url_tidal'])) {
                $trackUrls[] = TrackUrl::fromUrl($data['track_url_tidal']);
            }
            if (! empty($data['track_url_apple_music'])) {
                $trackUrls[] = TrackUrl::fromUrl($data['track_url_apple_music']);
            }

            // Validate at least one track URL
            if (empty($trackUrls)) {
                throw FrameworkException::create(
                    ErrorCode::VAL_REQUIRED_FIELD_MISSING,
                    'At least one track URL is required'
                );
            }

            // Update campaign - create new instance with updated values
            $updatedCampaign = new PreSaveCampaign(
                id: $campaign->id,
                title: $data['title'],
                artistName: $data['artist_name'],
                coverImageUrl: $data['cover_image_url'],
                releaseDate: Timestamp::fromDateTime(new \DateTimeImmutable($data['release_date'])),
                trackUrls: $trackUrls,
                status: CampaignStatus::from($data['status']),
                createdAt: $campaign->createdAt,
                updatedAt: Timestamp::now(),
                description: ! empty($data['description']) ? $data['description'] : null,
                startDate: ! empty($data['start_date']) ? Timestamp::fromDateTime(new \DateTimeImmutable($data['start_date'])) : null
            );

            $this->repository->save($updatedCampaign);

            return new Redirect('/admin/presave-campaigns/' . $id);
        } catch (FrameworkException $e) {
            throw $e;
        } catch (\Throwable $e) {
            throw FrameworkException::create(
                ErrorCode::DB_QUERY_FAILED,
                'Failed to update campaign'
            )->withContext($e->getMessage());
        }
    }

    /**
     * Delete campaign
     */
    #[Route('/admin/presave-campaigns/{id}/delete', Method::POST)]
    public function destroy(int $id): Redirect
    {
        $this->repository->delete($id);

        return new Redirect('/admin/presave-campaigns');
    }

    /**
     * Publish campaign (custom action)
     */
    #[Route('/admin/presave-campaigns/{id}/publish', Method::POST)]
    public function publish(int $id): Redirect
    {
        try {
            $campaign = $this->repository->findById($id);

            if ($campaign === null) {
                throw FrameworkException::create(
                    ErrorCode::ENTITY_NOT_FOUND,
                    'Campaign not found'
                )->withData(['campaign_id' => $id]);
            }

            if ($campaign->status !== CampaignStatus::DRAFT) {
                throw FrameworkException::create(
                    ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
                    'Only draft campaigns can be published'
                )->withData([
                    'campaign_id' => $id,
                    'current_status' => $campaign->status->value,
                ]);
            }

            $publishedCampaign = $campaign->publish();
            $this->repository->save($publishedCampaign);

            return new Redirect('/admin/presave-campaigns/' . $id);
        } catch (FrameworkException $e) {
            throw $e;
        } catch (\Throwable $e) {
            throw FrameworkException::create(
                ErrorCode::DB_QUERY_FAILED,
                'Failed to publish campaign'
            )->withContext($e->getMessage());
        }
    }

    /**
     * Cancel campaign (custom action)
     */
    #[Route('/admin/presave-campaigns/{id}/cancel', Method::POST)]
    public function cancel(int $id): Redirect
    {
        try {
            $campaign = $this->repository->findById($id);

            if ($campaign === null) {
                throw FrameworkException::create(
                    ErrorCode::ENTITY_NOT_FOUND,
                    'Campaign not found'
                )->withData(['campaign_id' => $id]);
            }

            if (! in_array($campaign->status, [CampaignStatus::DRAFT, CampaignStatus::SCHEDULED, CampaignStatus::ACTIVE], true)) {
                throw FrameworkException::create(
                    ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
                    'Campaign cannot be cancelled in current status'
                )->withData([
                    'campaign_id' => $id,
                    'current_status' => $campaign->status->value,
                ]);
            }

            $cancelledCampaign = $campaign->cancel();
            $this->repository->save($cancelledCampaign);

            return new Redirect('/admin/presave-campaigns/' . $id);
        } catch (FrameworkException $e) {
            throw $e;
        } catch (\Throwable $e) {
            throw FrameworkException::create(
                ErrorCode::DB_QUERY_FAILED,
                'Failed to cancel campaign'
            )->withContext($e->getMessage());
        }
    }
}
