<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\Collection;
use App\Models\User;
use App\Services\CollectionBackgroundJobService;
use App\Services\CollectionCampaignService;
use App\Services\CollectionService;
use App\Services\CollectionWorkflowService;
use Illuminate\Validation\ValidationException;

class CollectionBulkActionService
{
    public function __construct(
        private readonly CollectionService $collections,
        private readonly CollectionCampaignService $campaigns,
        private readonly CollectionWorkflowService $workflow,
        private readonly CollectionBackgroundJobService $backgroundJobs,
    ) {
    }

    public function apply(User $user, array $payload): array
    {
        $action = (string) $payload['action'];
        $collectionIds = collect($payload['collection_ids'] ?? [])
            ->map(static fn ($id): int => (int) $id)
            ->filter(static fn (int $id): bool => $id > 0)
            ->unique()
            ->values();

        if ($collectionIds->isEmpty()) {
            throw ValidationException::withMessages([
                'collection_ids' => 'Select at least one collection.',
            ]);
        }

        $collections = Collection::query()
            ->ownedBy((int) $user->id)
            ->whereIn('id', $collectionIds->all())
            ->get()
            ->keyBy(fn (Collection $collection): int => (int) $collection->id);

        if ($collections->count() !== $collectionIds->count()) {
            throw ValidationException::withMessages([
                'collection_ids' => 'One or more selected collections are unavailable.',
            ]);
        }

        $items = [];
        $updatedCollections = $collectionIds->map(function (int $collectionId) use ($collections, $user, $payload, $action, &$items): Collection {
            $collection = $collections->get($collectionId);

            if (! $collection instanceof Collection) {
                throw ValidationException::withMessages([
                    'collection_ids' => 'One or more selected collections are unavailable.',
                ]);
            }

            $updated = match ($action) {
                'archive' => $this->archive($collection->loadMissing('user'), $user),
                'assign_campaign' => $this->assignCampaign($collection->loadMissing('user'), $payload, $user),
                'update_lifecycle' => $this->updateLifecycle($collection->loadMissing('user'), $payload, $user),
                'request_ai_review' => $this->requestAiReview($collection->loadMissing('user'), $user, $items),
                'mark_editorial_review' => $this->markEditorialReview($collection->loadMissing('user'), $user),
                default => throw ValidationException::withMessages([
                    'action' => 'Unsupported bulk action.',
                ]),
            };
            if ($action !== 'request_ai_review') {
                $items[] = [
                    'collection_id' => (int) $updated->id,
                    'action' => $action,
                ];
            }

            return $updated;
        });

        return [
            'action' => $action,
            'count' => $updatedCollections->count(),
            'items' => $items,
            'collections' => $updatedCollections,
            'message' => $this->messageFor($action, $updatedCollections->count()),
        ];
    }

    private function archive(Collection $collection, User $user): Collection
    {
        return $this->collections->updateCollection($collection, [
            'lifecycle_state' => Collection::LIFECYCLE_ARCHIVED,
            'archived_at' => now(),
        ], $user);
    }

    private function assignCampaign(Collection $collection, array $payload, User $user): Collection
    {
        return $this->campaigns->updateCampaign($collection, [
            'campaign_key' => (string) $payload['campaign_key'],
            'campaign_label' => $payload['campaign_label'] ?? null,
        ], $user);
    }

    private function updateLifecycle(Collection $collection, array $payload, User $user): Collection
    {
        $lifecycleState = (string) $payload['lifecycle_state'];
        $attributes = [
            'lifecycle_state' => $lifecycleState,
        ];

        if ($lifecycleState === Collection::LIFECYCLE_ARCHIVED) {
            $attributes['archived_at'] = now();
        } else {
            $attributes['archived_at'] = null;
        }

        if ($lifecycleState === Collection::LIFECYCLE_PUBLISHED) {
            $attributes['visibility'] = Collection::VISIBILITY_PUBLIC;
            $attributes['published_at'] = $collection->published_at ?? now();
            $attributes['expired_at'] = null;
        }

        if ($lifecycleState === Collection::LIFECYCLE_DRAFT) {
            $attributes['visibility'] = Collection::VISIBILITY_PRIVATE;
            $attributes['expired_at'] = null;
            $attributes['unpublished_at'] = null;
        }

        return $this->collections->updateCollection($collection, $attributes, $user);
    }

    private function requestAiReview(Collection $collection, User $user, array &$items): Collection
    {
        $result = $this->backgroundJobs->dispatchQualityRefresh($collection, $user);
        $items[] = [
            'collection_id' => (int) $collection->id,
            'job' => $result['job'] ?? 'quality_refresh',
            'status' => $result['status'] ?? 'queued',
        ];

        return $collection->fresh()->loadMissing('user');
    }

    private function markEditorialReview(Collection $collection, User $user): Collection
    {
        if ($collection->workflow_state === Collection::WORKFLOW_IN_REVIEW) {
            return $collection->fresh()->loadMissing('user');
        }

        return $this->workflow->update($collection, [
            'workflow_state' => Collection::WORKFLOW_IN_REVIEW,
        ], $user);
    }

    private function messageFor(string $action, int $count): string
    {
        return match ($action) {
            'archive' => $count === 1 ? 'Collection archived.' : sprintf('%d collections archived.', $count),
            'assign_campaign' => $count === 1 ? 'Campaign assigned.' : sprintf('Campaign assigned to %d collections.', $count),
            'update_lifecycle' => $count === 1 ? 'Collection lifecycle updated.' : sprintf('Lifecycle updated for %d collections.', $count),
            'request_ai_review' => $count === 1 ? 'AI review requested.' : sprintf('AI review requested for %d collections.', $count),
            'mark_editorial_review' => $count === 1 ? 'Collection marked for editorial review.' : sprintf('%d collections marked for editorial review.', $count),
            default => 'Bulk action completed.',
        };
    }
}