<?php

declare(strict_types=1);

namespace App\Http\Controllers\Settings;

use App\Http\Controllers\Controller;
use App\Http\Requests\Collections\AttachCollectionArtworksRequest;
use App\Http\Requests\Collections\ReorderCollectionArtworksRequest;
use App\Http\Requests\Collections\ReorderProfileCollectionsRequest;
use App\Http\Requests\Collections\SmartCollectionRulesRequest;
use App\Http\Requests\Collections\StoreCollectionRequest;
use App\Http\Requests\Collections\UpdateCollectionCampaignRequest;
use App\Http\Requests\Collections\UpdateCollectionEntityLinksRequest;
use App\Http\Requests\Collections\UpdateCollectionRequest;
use App\Http\Requests\Collections\UpdateCollectionLifecycleRequest;
use App\Http\Requests\Collections\UpdateCollectionLinkedCollectionsRequest;
use App\Http\Requests\Collections\UpdateCollectionPresentationRequest;
use App\Http\Requests\Collections\UpdateCollectionSeriesRequest;
use App\Models\Artwork;
use App\Models\Collection;
use App\Models\Group;
use App\Services\CollectionCollaborationService;
use App\Services\CollectionCampaignService;
use App\Services\CollectionCommentService;
use App\Services\CollectionLinkService;
use App\Services\CollectionLinkedCollectionsService;
use App\Services\CollectionMergeService;
use App\Services\CollectionSeriesService;
use App\Services\CollectionSubmissionService;
use App\Services\CollectionService;
use App\Support\AvatarUrl;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;

class CollectionManageController extends Controller
{
    public function __construct(
        private readonly CollectionService $collections,
        private readonly CollectionCampaignService $campaigns,
        private readonly CollectionCollaborationService $collaborators,
        private readonly CollectionSubmissionService $submissions,
        private readonly CollectionCommentService $comments,
        private readonly CollectionLinkService $entityLinks,
        private readonly CollectionLinkedCollectionsService $linkedCollections,
        private readonly CollectionMergeService $merge,
        private readonly CollectionSeriesService $series,
    ) {
    }

    public function create(Request $request)
    {
        $this->authorize('create', Collection::class);

        $initialMode = $request->query('mode') === Collection::MODE_SMART
            ? Collection::MODE_SMART
            : Collection::MODE_MANUAL;
        $group = null;

        if ($request->filled('group')) {
            $group = Group::query()->with(['owner.profile', 'members'])->where('slug', (string) $request->query('group'))->first();
            abort_if($group && ! $group->canManageCollections($request->user()), 403);
        }

        return Inertia::render('Collection/CollectionManage', [
            'mode' => 'create',
            'collection' => null,
            'layoutModules' => $this->collections->getLayoutModuleDefinitions(),
            'attachedArtworks' => [],
            'availableArtworks' => [],
            'smartPreview' => null,
            'smartRuleOptions' => $this->collections->getSmartRuleOptions($request->user()),
            'initialMode' => $initialMode,
            'featuredLimit' => (int) config('collections.featured_limit', 3),
            'owner' => $this->ownerPayload($request, $group),
            'members' => [],
            'submissions' => [],
            'comments' => [],
            'duplicateCandidates' => [],
            'canonicalTarget' => null,
            'inviteExpiryDays' => (int) config('collections.invites.expires_after_days', 7),
            'endpoints' => [
                'store' => route('settings.collections.store', $group ? ['group' => $group->slug] : []),
                'smartPreview' => route('settings.collections.smart.preview'),
                'profileCollections' => route('profile.tab', [
                    'username' => strtolower((string) $request->user()->username),
                    'tab' => 'collections',
                ]),
            ],
        ])->rootView('collections');
    }

    public function show(Request $request, Collection $collection)
    {
        $this->authorize('manageArtworks', $collection);
        $collection->loadMissing(['user.profile', 'coverArtwork']);

        return Inertia::render('Collection/CollectionManage', [
            'mode' => 'edit',
            'collection' => $this->collections->mapCollectionDetailPayload($collection, true),
            'layoutModules' => $this->collections->mapCollectionDetailPayload($collection, true)['layout_modules'],
            'attachedArtworks' => $this->collections->mapAttachedArtworks($collection),
            'availableArtworks' => $this->collections->getAvailableArtworkOptions($collection, $request->user()),
            'smartPreview' => $collection->isSmart() && is_array($collection->smart_rules_json)
                ? $this->collections->previewSmartCollection($request->user(), $collection->smart_rules_json)
                : null,
            'smartRuleOptions' => $this->collections->getSmartRuleOptions($request->user()),
            'initialMode' => $collection->mode,
            'featuredLimit' => (int) config('collections.featured_limit', 3),
            'owner' => $this->ownerPayload($request),
            'members' => $this->collaborators->mapMembers($collection, $request->user()),
            'submissions' => $this->submissions->mapSubmissions($collection, $request->user()),
            'comments' => $this->comments->mapComments($collection, $request->user()),
            'duplicateCandidates' => $this->merge->reviewCandidates($collection->loadMissing('user'), true),
            'canonicalTarget' => $collection->canonical_collection_id
                ? $this->collections->mapCollectionCardPayloads([
                    Collection::query()->findOrFail((int) $collection->canonical_collection_id)->loadMissing('user'),
                ], true)[0]
                : null,
            'linkedCollections' => $this->collections->mapCollectionCardPayloads($this->linkedCollections->linkedCollections($collection), true),
            'linkedCollectionOptions' => $this->collections->mapCollectionCardPayloads($this->linkedCollections->manageableLinkOptions($collection, $request->user()), true),
            'entityLinks' => $this->entityLinks->links($collection, false),
            'entityLinkOptions' => $this->entityLinks->manageableOptions($collection),
            'inviteExpiryDays' => (int) config('collections.invites.expires_after_days', 7),
            'endpoints' => [
                'update' => route('settings.collections.update', ['collection' => $collection->id]),
                'updatePresentation' => route('settings.collections.presentation', ['collection' => $collection->id]),
                'updateCampaign' => route('settings.collections.campaign', ['collection' => $collection->id]),
                'updateSeries' => route('settings.collections.series', ['collection' => $collection->id]),
                'updateLifecycle' => route('settings.collections.lifecycle', ['collection' => $collection->id]),
                'syncLinkedCollections' => route('settings.collections.linked.sync', ['collection' => $collection->id]),
                'syncEntityLinks' => route('settings.collections.entity-links.sync', ['collection' => $collection->id]),
                'delete' => route('settings.collections.destroy', ['collection' => $collection->id]),
                'attach' => route('settings.collections.artworks.attach', ['collection' => $collection->id]),
                'reorder' => route('settings.collections.artworks.reorder', ['collection' => $collection->id]),
                'available' => route('settings.collections.artworks.available', ['collection' => $collection->id]),
                'removePattern' => route('settings.collections.artworks.remove', ['collection' => $collection->id, 'artwork' => '__ARTWORK__']),
                'edit' => route('settings.collections.edit', ['collection' => $collection->id]),
                'feature' => route('settings.collections.feature', ['collection' => $collection->id]),
                'unfeature' => route('settings.collections.unfeature', ['collection' => $collection->id]),
                'smartPreview' => route('settings.collections.smart.preview'),
                'aiSuggestTitle' => route('settings.collections.ai.suggest-title', ['collection' => $collection->id]),
                'aiSuggestSummary' => route('settings.collections.ai.suggest-summary', ['collection' => $collection->id]),
                'aiSuggestCover' => route('settings.collections.ai.suggest-cover', ['collection' => $collection->id]),
                'aiSuggestGrouping' => route('settings.collections.ai.suggest-grouping', ['collection' => $collection->id]),
                'aiSuggestRelatedArtworks' => route('settings.collections.ai.suggest-related-artworks', ['collection' => $collection->id]),
                'aiSuggestTags' => route('settings.collections.ai.suggest-tags', ['collection' => $collection->id]),
                'aiSuggestSeoDescription' => route('settings.collections.ai.suggest-seo-description', ['collection' => $collection->id]),
                'aiExplainSmartRules' => route('settings.collections.ai.explain-smart-rules', ['collection' => $collection->id]),
                'aiSuggestSplitThemes' => route('settings.collections.ai.suggest-split-themes', ['collection' => $collection->id]),
                'aiSuggestMergeIdea' => route('settings.collections.ai.suggest-merge-idea', ['collection' => $collection->id]),
                'aiQualityReview' => route('settings.collections.ai.quality-review', ['collection' => $collection->id]),
                'updateSmartRules' => route('settings.collections.smart.rules', ['collection' => $collection->id]),
                'inviteMember' => route('settings.collections.members.store', ['collection' => $collection->id]),
                'memberUpdatePattern' => route('settings.collections.members.update', ['collection' => $collection->id, 'member' => '__MEMBER__']),
                'memberTransferPattern' => route('settings.collections.members.transfer', ['collection' => $collection->id, 'member' => '__MEMBER__']),
                'memberDeletePattern' => route('settings.collections.members.destroy', ['collection' => $collection->id, 'member' => '__MEMBER__']),
                'acceptMemberPattern' => route('settings.collections.members.accept', ['member' => '__MEMBER__']),
                'declineMemberPattern' => route('settings.collections.members.decline', ['member' => '__MEMBER__']),
                'adminModerationUpdate' => $this->isAdmin($request) ? route('api.admin.collections.moderation.update', ['collection' => $collection->id]) : null,
                'adminInteractionsUpdate' => $this->isAdmin($request) ? route('api.admin.collections.interactions.update', ['collection' => $collection->id]) : null,
                'adminUnfeature' => $this->isAdmin($request) ? route('api.admin.collections.unfeature', ['collection' => $collection->id]) : null,
                'adminMemberRemovePattern' => $this->isAdmin($request) ? route('api.admin.collections.members.destroy', ['collection' => $collection->id, 'member' => '__MEMBER__']) : null,
                'submissionStore' => route('collections.submissions.store', ['collection' => $collection->id]),
                'submissionApprovePattern' => route('collections.submissions.approve', ['submission' => '__SUBMISSION__']),
                'submissionRejectPattern' => route('collections.submissions.reject', ['submission' => '__SUBMISSION__']),
                'submissionDeletePattern' => route('collections.submissions.destroy', ['submission' => '__SUBMISSION__']),
                'commentsIndex' => route('collections.comments.index', ['collection' => $collection->id]),
                'commentsStore' => route('collections.comments.store', ['collection' => $collection->id]),
                'commentDeletePattern' => route('collections.comments.destroy', ['collection' => $collection->id, 'comment' => '__COMMENT__']),
                'public' => route('profile.collections.show', [
                    'username' => strtolower((string) $collection->user->username),
                    'slug' => $collection->slug,
                ]),
                'dashboard' => route('settings.collections.dashboard'),
                'analytics' => route('settings.collections.analytics', ['collection' => $collection->id]),
                'history' => route('settings.collections.history', ['collection' => $collection->id]),
                'canonicalize' => route('settings.collections.canonicalize', ['collection' => $collection->id]),
                'merge' => route('settings.collections.merge', ['collection' => $collection->id]),
                'rejectDuplicate' => route('settings.collections.merge.reject', ['collection' => $collection->id]),
                'staffSurfaces' => $this->isAdmin($request) ? route('settings.collections.surfaces.index') : null,
                'staffProgramming' => $this->isAdmin($request) ? route('staff.collections.programming') : null,
                'profileCollections' => route('profile.tab', [
                    'username' => strtolower((string) $collection->user->username),
                    'tab' => 'collections',
                ]),
            ],
            'viewer' => [
                'is_admin' => $this->isAdmin($request),
            ],
        ])->rootView('collections');
    }

    public function edit(Request $request, Collection $collection)
    {
        return $this->show($request, $collection);
    }

    public function store(StoreCollectionRequest $request): RedirectResponse|JsonResponse
    {
        $this->authorize('create', Collection::class);

        $collection = $this->collections->createCollection($request->user(), $request->validated());
        $redirectTo = (string) route('settings.collections.show', ['collection' => $collection->id]);

        return $this->jsonOrRedirect($request, [
            'ok' => true,
            'collection' => $this->collections->mapCollectionDetailPayload($collection->loadMissing('user'), true),
            'members' => $this->collaborators->mapMembers($collection, $request->user()),
            'submissions' => $this->submissions->mapSubmissions($collection, $request->user()),
            'comments' => $this->comments->mapComments($collection, $request->user()),
            'redirect' => $redirectTo,
        ], $redirectTo);
    }

    public function update(UpdateCollectionRequest $request, Collection $collection): RedirectResponse|JsonResponse
    {
        $this->authorize('update', $collection);

        $collection = $this->collections->updateCollection($collection->loadMissing('user'), $request->validated(), $request->user());
        $redirectTo = (string) route('settings.collections.show', ['collection' => $collection->id]);

        return $this->jsonOrRedirect($request, [
            'ok' => true,
            'collection' => $this->collections->mapCollectionDetailPayload($collection->loadMissing('user'), true),
            'attachedArtworks' => $this->collections->mapAttachedArtworks($collection),
            'members' => $this->collaborators->mapMembers($collection, $request->user()),
            'submissions' => $this->submissions->mapSubmissions($collection, $request->user()),
            'comments' => $this->comments->mapComments($collection, $request->user()),
            'redirect' => $redirectTo,
        ], $redirectTo);
    }

    public function updatePresentation(UpdateCollectionPresentationRequest $request, Collection $collection): JsonResponse
    {
        $this->authorize('update', $collection);

        return $this->updateScopedSettings($request, $collection, $request->validated());
    }

    public function updateCampaign(UpdateCollectionCampaignRequest $request, Collection $collection): JsonResponse
    {
        $this->authorize('update', $collection);

        $collection = $this->campaigns->updateCampaign($collection, $request->validated(), $request->user());

        return response()->json([
            'ok' => true,
            'collection' => $this->collections->mapCollectionDetailPayload($collection->loadMissing('user'), true),
            'campaign' => $this->campaigns->campaignSummary($collection),
            'attachedArtworks' => $this->collections->mapAttachedArtworks($collection),
            'members' => $this->collaborators->mapMembers($collection, $request->user()),
            'submissions' => $this->submissions->mapSubmissions($collection, $request->user()),
            'comments' => $this->comments->mapComments($collection, $request->user()),
        ]);
    }

    public function updateSeries(UpdateCollectionSeriesRequest $request, Collection $collection): JsonResponse
    {
        $this->authorize('update', $collection);

        $collection = $this->series->updateSeries($collection, $request->validated(), $request->user());

        return response()->json([
            'ok' => true,
            'collection' => $this->collections->mapCollectionDetailPayload($collection->loadMissing('user'), true),
            'series' => $this->series->summary($collection),
            'attachedArtworks' => $this->collections->mapAttachedArtworks($collection),
            'members' => $this->collaborators->mapMembers($collection, $request->user()),
            'submissions' => $this->submissions->mapSubmissions($collection, $request->user()),
            'comments' => $this->comments->mapComments($collection, $request->user()),
        ]);
    }

    public function updateLifecycle(UpdateCollectionLifecycleRequest $request, Collection $collection): JsonResponse
    {
        $this->authorize('update', $collection);

        return $this->updateScopedSettings($request, $collection, $request->validated());
    }

    public function syncLinkedCollections(UpdateCollectionLinkedCollectionsRequest $request, Collection $collection): JsonResponse
    {
        $this->authorize('update', $collection);

        $collection = $this->linkedCollections->syncLinks($collection->loadMissing('user'), $request->user(), $request->validated('related_collection_ids', []));

        return response()->json([
            'ok' => true,
            'collection' => $this->collections->mapCollectionDetailPayload($collection->loadMissing('user'), true),
            'linkedCollections' => $this->collections->mapCollectionCardPayloads($this->linkedCollections->linkedCollections($collection), true),
            'linkedCollectionOptions' => $this->collections->mapCollectionCardPayloads($this->linkedCollections->manageableLinkOptions($collection, $request->user()), true),
        ]);
    }

    public function syncEntityLinks(UpdateCollectionEntityLinksRequest $request, Collection $collection): JsonResponse
    {
        $this->authorize('update', $collection);

        $collection = $this->entityLinks->syncLinks($collection->loadMissing('user'), $request->user(), $request->validated('entity_links', []));

        return response()->json([
            'ok' => true,
            'collection' => $this->collections->mapCollectionDetailPayload($collection->loadMissing('user'), true),
            'entityLinks' => $this->entityLinks->links($collection, false),
            'entityLinkOptions' => $this->entityLinks->manageableOptions($collection),
        ]);
    }

    public function destroy(Request $request, Collection $collection): RedirectResponse|JsonResponse
    {
        $this->authorize('delete', $collection);

        $profileCollectionsUrl = (string) route('profile.tab', [
            'username' => strtolower((string) $collection->user->username),
            'tab' => 'collections',
        ]);

        $this->collections->deleteCollection($collection);

        return $this->jsonOrRedirect($request, [
            'ok' => true,
            'redirect' => $profileCollectionsUrl,
        ], $profileCollectionsUrl);
    }

    public function attachArtworks(AttachCollectionArtworksRequest $request, Collection $collection): JsonResponse
    {
        $this->authorize('manageArtworks', $collection);

        $collection = $this->collections->attachArtworks($collection->loadMissing('user'), $request->user(), $request->validated('artwork_ids'));

        return response()->json([
            'ok' => true,
            'collection' => $this->collections->mapCollectionDetailPayload($collection->loadMissing('user'), true),
            'attachedArtworks' => $this->collections->mapAttachedArtworks($collection),
            'availableArtworks' => $this->collections->getAvailableArtworkOptions($collection, $request->user()),
        ]);
    }

    public function artworkCollectionOptions(Request $request): JsonResponse
    {
        $artwork = $this->resolveArtworkFromRequest($request, 4);

        if ((int) ($artwork->group_id ?? 0) > 0) {
            abort_unless($artwork->group?->canManageCollections($request->user()) ?? false, 404);
        } else {
            abort_unless((int) $artwork->user_id === (int) $request->user()->id, 404);
        }

        return response()->json([
            'data' => $this->collections->getCollectionOptionsForArtwork($request->user(), $artwork),
            'meta' => [
                'create_url' => route('settings.collections.create'),
                'artwork_id' => (int) $artwork->id,
            ],
        ]);
    }

    public function removeArtwork(Request $request, Collection $collection): JsonResponse
    {
        $this->authorize('manageArtworks', $collection);

        $artwork = $this->resolveArtworkFromRequest($request, 5);

        $isAttached = $collection->artworks()->where('artworks.id', $artwork->id)->exists();
        abort_unless($isAttached, 404);

        $collection = $this->collections->removeArtwork($collection->loadMissing('user'), $artwork);

        return response()->json([
            'ok' => true,
            'collection' => $this->collections->mapCollectionDetailPayload($collection->loadMissing('user'), true),
            'attachedArtworks' => $this->collections->mapAttachedArtworks($collection),
            'availableArtworks' => $this->collections->getAvailableArtworkOptions($collection, $request->user()),
        ]);
    }

    public function reorderArtworks(ReorderCollectionArtworksRequest $request, Collection $collection): JsonResponse
    {
        $this->authorize('manageArtworks', $collection);

        $collection = $this->collections->reorderArtworks($collection->loadMissing('user'), $request->validated('ordered_artwork_ids'));

        return response()->json([
            'ok' => true,
            'collection' => $this->collections->mapCollectionDetailPayload($collection->loadMissing('user'), true),
            'attachedArtworks' => $this->collections->mapAttachedArtworks($collection),
        ]);
    }

    public function availableArtworks(Request $request, Collection $collection): JsonResponse
    {
        $this->authorize('manageArtworks', $collection);

        $request->validate([
            'search' => ['nullable', 'string', 'max:120'],
        ]);

        return response()->json([
            'data' => $this->collections->getAvailableArtworkOptions($collection, $request->user(), (string) $request->input('search', '')),
        ]);
    }

    public function feature(Request $request, Collection $collection): JsonResponse
    {
        $this->authorize('update', $collection);

        $collection = $this->collections->featureCollection($collection);

        return response()->json([
            'ok' => true,
            'collection' => $this->collections->mapCollectionDetailPayload($collection->loadMissing('user'), true),
        ]);
    }

    public function unfeature(Request $request, Collection $collection): JsonResponse
    {
        $this->authorize('update', $collection);

        $collection = $this->collections->unfeatureCollection($collection);

        return response()->json([
            'ok' => true,
            'collection' => $this->collections->mapCollectionDetailPayload($collection->loadMissing('user'), true),
        ]);
    }

    public function reorderProfile(ReorderProfileCollectionsRequest $request): JsonResponse
    {
        $this->collections->reorderProfileCollections($request->user(), $request->validated('collection_ids'));

        $items = $this->collections->getProfileCollections($request->user(), $request->user(), 24);

        return response()->json([
            'ok' => true,
            'collections' => $this->collections->mapCollectionCardPayloads($items, true),
        ]);
    }

    public function smartPreview(SmartCollectionRulesRequest $request): JsonResponse
    {
        return response()->json([
            'ok' => true,
            'preview' => $this->collections->previewSmartCollection($request->user(), $request->validated('smart_rules_json')),
        ]);
    }

    public function updateSmartRules(SmartCollectionRulesRequest $request, Collection $collection): JsonResponse
    {
        $this->authorize('update', $collection);

        $collection = $this->collections->updateCollection($collection->loadMissing('user'), [
            'title' => $collection->title,
            'slug' => $collection->slug,
            'description' => $collection->description,
            'subtitle' => $collection->subtitle,
            'summary' => $collection->summary,
            'visibility' => $collection->visibility,
            'mode' => Collection::MODE_SMART,
            'sort_mode' => (string) (($request->validated('smart_rules_json')['sort'] ?? null) ?: $collection->sort_mode),
            'smart_rules_json' => $request->validated('smart_rules_json'),
        ]);

        return response()->json([
            'ok' => true,
            'collection' => $this->collections->mapCollectionDetailPayload($collection->loadMissing('user'), true),
            'preview' => $this->collections->previewSmartCollection($request->user(), $collection->smart_rules_json ?? []),
        ]);
    }

    private function ownerPayload(Request $request): array
    {
        $user = $request->user();

        if ($request->filled('group')) {
            $group = Group::query()->with('owner.profile')->where('slug', (string) $request->query('group'))->first();

            if ($group && $group->canManageCollections($user)) {
                return [
                    'id' => $group->id,
                    'username' => null,
                    'name' => $group->name,
                    'avatar_url' => $group->avatarUrl(),
                    'group_slug' => $group->slug,
                    'profile_url' => $group->publicUrl(),
                ];
            }
        }

        return [
            'id' => $user->id,
            'username' => $user->username,
            'name' => $user->name,
            'avatar_url' => AvatarUrl::forUser((int) $user->id, $user->profile?->avatar_hash, 96),
        ];
    }

    private function isAdmin(Request $request): bool
    {
        $user = $request->user();

        return $user !== null && method_exists($user, 'hasRole') && $user->hasRole('admin');
    }

    private function updateScopedSettings(Request $request, Collection $collection, array $attributes): JsonResponse
    {
        $collection = $this->collections->updateCollection($collection->loadMissing('user'), $attributes, $request->user());

        return response()->json([
            'ok' => true,
            'collection' => $this->collections->mapCollectionDetailPayload($collection->loadMissing('user'), true),
            'attachedArtworks' => $this->collections->mapAttachedArtworks($collection),
            'members' => $this->collaborators->mapMembers($collection, $request->user()),
            'submissions' => $this->submissions->mapSubmissions($collection, $request->user()),
            'comments' => $this->comments->mapComments($collection, $request->user()),
        ]);
    }

    private function jsonOrRedirect(Request $request, array $payload, string $redirectTo): RedirectResponse|JsonResponse
    {
        if ($request->expectsJson()) {
            return response()->json($payload);
        }

        return redirect($redirectTo);
    }

    private function resolveArtworkFromRequest(Request $request, int $fallbackSegment): Artwork
    {
        $routeValue = $request->route('artwork');

        if ($routeValue instanceof Artwork) {
            return $routeValue;
        }

        $artworkId = is_scalar($routeValue)
            ? (int) $routeValue
            : (int) $request->segment($fallbackSegment);

        return Artwork::query()->findOrFail($artworkId);
    }
}
