<?php

namespace App\Http\Controllers\Api\Tenant;

use App\Http\Controllers\Controller;
use App\Http\Requests\Tenant\TaskStoreRequest;
use App\Http\Requests\Tenant\TaskUpdateRequest;
use App\Http\Resources\Tenant\TaskResource;
use App\Models\Event;
use App\Models\Task;
use App\Models\TaskCollection;
use App\Models\Tenant;
use App\Support\ApiError;
use App\Support\TenantMemberPermissions;
use App\Support\TenantRequestResolver;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Symfony\Component\HttpFoundation\Response;

class TaskController extends Controller
{
    /**
     * Display a listing of the tenant's tasks.
     */
    public function index(Request $request): AnonymousResourceCollection
    {
        $tenantId = $this->currentTenant($request)->id;

        $query = Task::query()
            ->where(function ($inner) use ($tenantId) {
                $inner->whereNull('tenant_id')
                    ->orWhere('tenant_id', $tenantId);
            })
            ->with(['taskCollection', 'assignedEvents', 'eventType', 'emotion'])
            ->orderByRaw('tenant_id is null desc')
            ->orderBy('sort_order')
            ->orderBy('created_at', 'desc');

        // Search and filters
        if ($search = $request->get('search')) {
            $query->where(function ($inner) use ($search) {
                $like = '%'.$search.'%';
                $inner->where('title->de', 'like', $like)
                    ->orWhere('title->en', 'like', $like)
                    ->orWhere('description->de', 'like', $like)
                    ->orWhere('description->en', 'like', $like);
            });
        }

        if ($collectionId = $request->get('collection_id')) {
            $query->whereHas('taskCollection', fn ($q) => $q->where('id', $collectionId));
        }

        if ($eventId = $request->get('event_id')) {
            $query->whereHas('assignedEvents', fn ($q) => $q->where('id', $eventId));
        }

        $perPage = $request->get('per_page', 15);
        $tasks = $query->paginate($perPage);

        return TaskResource::collection($tasks);
    }

    /**
     * Store a newly created task in storage.
     */
    public function store(TaskStoreRequest $request): JsonResponse
    {
        TenantMemberPermissions::ensureTenantPermission($request, 'tasks:manage');

        $tenant = $this->currentTenant($request);
        $collectionId = $request->input('collection_id');
        $collection = $collectionId ? $this->resolveAccessibleCollection($request, $collectionId) : null;

        $payload = $this->prepareTaskPayload($request->validated(), $tenant->id);
        $payload['tenant_id'] = $tenant->id;

        if ($collection) {
            $payload['collection_id'] = $collection->id;
            $payload['source_collection_id'] = $collection->source_collection_id ?? $collection->id;
        }

        $task = Task::create($payload);

        $task->load(['taskCollection', 'assignedEvents', 'eventType', 'emotion']);

        return response()->json([
            'message' => 'Task erfolgreich erstellt.',
            'data' => new TaskResource($task),
        ], 201);
    }

    /**
     * Display the specified task.
     */
    public function show(Request $request, Task $task): JsonResponse
    {
        if ($task->tenant_id && $task->tenant_id !== $this->currentTenant($request)->id) {
            abort(404, 'Task nicht gefunden.');
        }

        $task->load(['taskCollection', 'assignedEvents', 'eventType', 'emotion']);

        return response()->json(new TaskResource($task));
    }

    /**
     * Update the specified task in storage.
     */
    public function update(TaskUpdateRequest $request, Task $task): JsonResponse
    {
        TenantMemberPermissions::ensureTenantPermission($request, 'tasks:manage');

        $tenant = $this->currentTenant($request);

        if ($task->tenant_id !== $tenant->id) {
            abort(404, 'Task nicht gefunden.');
        }

        $collectionId = $request->input('collection_id');
        $collection = $collectionId ? $this->resolveAccessibleCollection($request, $collectionId) : null;

        $payload = $this->prepareTaskPayload($request->validated(), $tenant->id, $task);

        if ($collection) {
            $payload['collection_id'] = $collection->id;
            $payload['source_collection_id'] = $collection->source_collection_id ?? $collection->id;
        }

        $task->update($payload);

        $task->load(['taskCollection', 'assignedEvents', 'eventType', 'emotion']);

        return response()->json([
            'message' => 'Task erfolgreich aktualisiert.',
            'data' => new TaskResource($task),
        ]);
    }

    /**
     * Remove the specified task from storage.
     */
    public function destroy(Request $request, Task $task): JsonResponse
    {
        TenantMemberPermissions::ensureTenantPermission($request, 'tasks:manage');

        if ($task->tenant_id !== $this->currentTenant($request)->id) {
            abort(404, 'Task nicht gefunden.');
        }

        $task->delete();

        return response()->json([
            'message' => 'Task erfolgreich gelöscht.',
        ]);
    }

    /**
     * Assign task to an event.
     */
    public function assignToEvent(Request $request, Task $task, Event $event): JsonResponse
    {
        TenantMemberPermissions::ensureEventPermission($request, $event, 'tasks:manage');

        $tenantId = $this->currentTenant($request)->id;

        if (($task->tenant_id && $task->tenant_id !== $tenantId) || $event->tenant_id !== $tenantId) {
            abort(404);
        }

        if ($task->assignedEvents()->where('event_id', $event->id)->exists()) {
            return response()->json(['message' => 'Task ist bereits diesem Event zugewiesen.'], 409);
        }

        $task->assignedEvents()->attach($event->id);

        return response()->json([
            'message' => 'Task erfolgreich dem Event zugewiesen.',
        ]);
    }

    /**
     * Bulk assign tasks to an event.
     */
    public function bulkAssignToEvent(Request $request, Event $event): JsonResponse
    {
        TenantMemberPermissions::ensureEventPermission($request, $event, 'tasks:manage');

        $tenantId = $this->currentTenant($request)->id;

        if ($event->tenant_id !== $tenantId) {
            abort(404);
        }

        $taskIds = $request->input('task_ids', []);
        if (empty($taskIds)) {
            return ApiError::response(
                'task_ids_missing',
                'Keine Aufgaben angegeben',
                'Bitte wähle mindestens eine Aufgabe aus.',
                Response::HTTP_BAD_REQUEST
            );
        }

        $tasks = Task::whereIn('id', $taskIds)
            ->where(function ($query) use ($tenantId) {
                $query->whereNull('tenant_id')->orWhere('tenant_id', $tenantId);
            })
            ->get();

        $attached = 0;
        foreach ($tasks as $task) {
            if (! $task->assignedEvents()->where('event_id', $event->id)->exists()) {
                $task->assignedEvents()->attach($event->id);
                $attached++;
            }
        }

        return response()->json([
            'message' => "{$attached} Tasks dem Event zugewiesen.",
        ]);
    }

    /**
     * Get tasks for a specific event.
     */
    public function forEvent(Request $request, Event $event): AnonymousResourceCollection
    {
        if ($event->tenant_id !== $this->currentTenant($request)->id) {
            abort(404);
        }

        $tasks = Task::whereHas('assignedEvents', fn ($q) => $q->where('event_id', $event->id))
            ->with(['taskCollection', 'eventType', 'emotion'])
            ->orderBy('tasks.id')
            ->paginate($request->get('per_page', 15));

        return TaskResource::collection($tasks);
    }

    public function bulkDetachFromEvent(Request $request, Event $event): JsonResponse
    {
        TenantMemberPermissions::ensureEventPermission($request, $event, 'tasks:manage');

        $tenantId = $this->currentTenant($request)->id;

        if ($event->tenant_id !== $tenantId) {
            abort(404);
        }

        $taskIds = $request->input('task_ids', []);

        if (empty($taskIds)) {
            return ApiError::response(
                'task_ids_missing',
                'Keine Aufgaben angegeben',
                'Bitte wähle mindestens eine Aufgabe aus.',
                Response::HTTP_BAD_REQUEST
            );
        }

        $detached = $event->tasks()->whereIn('tasks.id', $taskIds)->detach();

        return response()->json([
            'message' => "{$detached} Tasks vom Event entfernt.",
        ]);
    }

    public function reorderForEvent(Request $request, Event $event): JsonResponse
    {
        TenantMemberPermissions::ensureEventPermission($request, $event, 'tasks:manage');

        $tenantId = $this->currentTenant($request)->id;

        if ($event->tenant_id !== $tenantId) {
            abort(404);
        }

        $taskIds = $request->input('task_ids', []);

        if (empty($taskIds) || ! is_array($taskIds)) {
            return ApiError::response(
                'task_ids_missing',
                'Keine Aufgaben angegeben',
                'Bitte wähle mindestens eine Aufgabe aus.',
                Response::HTTP_BAD_REQUEST
            );
        }

        return response()->json([
            'message' => 'Reihenfolge gespeichert.',
        ]);
    }

    /**
     * Get tasks from a specific collection.
     */
    public function fromCollection(Request $request, TaskCollection $collection): AnonymousResourceCollection
    {
        if ($collection->tenant_id && $collection->tenant_id !== $this->currentTenant($request)->id) {
            abort(404);
        }

        $tasks = $collection->tasks()
            ->with(['assignedEvents', 'eventType'])
            ->orderBy('created_at', 'desc')
            ->paginate($request->get('per_page', 15));

        return TaskResource::collection($tasks);
    }

    protected function resolveAccessibleCollection(Request $request, int|string $collectionId): TaskCollection
    {
        return TaskCollection::where('id', $collectionId)
            ->where(function ($query) use ($request) {
                $query->whereNull('tenant_id');

                $tenantId = $this->currentTenant($request)->id;

                if ($tenantId) {
                    $query->orWhere('tenant_id', $tenantId);
                }
            })
            ->firstOrFail();
    }

    protected function currentTenant(Request $request): Tenant
    {
        return TenantRequestResolver::resolve($request);
    }

    protected function prepareTaskPayload(array $data, int $tenantId, ?Task $original = null): array
    {
        if (array_key_exists('title', $data)) {
            $data['title'] = $this->normalizeTranslations($data['title'], $original?->title);
        } elseif (array_key_exists('title_translations', $data)) {
            $data['title'] = $this->normalizeTranslations($data['title_translations'], $original?->title);
        }

        if (array_key_exists('description', $data)) {
            $data['description'] = $this->normalizeTranslations($data['description'], $original?->description, true);
        } elseif (array_key_exists('description_translations', $data)) {
            $data['description'] = $this->normalizeTranslations(
                $data['description_translations'],
                $original?->description,
                true
            );
        }

        if (array_key_exists('example_text', $data)) {
            $data['example_text'] = $this->normalizeTranslations($data['example_text'], $original?->example_text, true);
        } elseif (array_key_exists('example_text_translations', $data)) {
            $data['example_text'] = $this->normalizeTranslations(
                $data['example_text_translations'],
                $original?->example_text,
                true
            );
        }

        unset(
            $data['title_translations'],
            $data['description_translations'],
            $data['example_text_translations']
        );

        if (! array_key_exists('difficulty', $data) || $data['difficulty'] === null) {
            $data['difficulty'] = $original?->difficulty ?? 'easy';
        }

        if (! array_key_exists('priority', $data) || $data['priority'] === null) {
            $data['priority'] = $original?->priority ?? 'medium';
        }

        if (array_key_exists('emotion_id', $data) && empty($data['emotion_id'])) {
            $data['emotion_id'] = null;
        }

        return $data;
    }

    /**
     * @param  array<string, string>|null  $fallback
     * @return array<string, string>|null
     */
    protected function normalizeTranslations(mixed $value, ?array $fallback = null, bool $allowNull = false): ?array
    {
        if ($allowNull && ($value === null || $value === '')) {
            return null;
        }

        if (is_array($value)) {
            $filtered = array_filter(
                $value,
                static fn ($text) => is_string($text) && $text !== ''
            );

            if (! empty($filtered)) {
                return $filtered;
            }

            return $allowNull ? null : ($fallback ?? []);
        }

        if (is_string($value) && $value !== '') {
            $locale = app()->getLocale() ?: 'de';

            return [
                $locale => $value,
            ];
        }

        return $allowNull ? null : $fallback;
    }
}
