<?php

namespace App\Http\Controllers;

use App\Models\TaskSubmission;
use App\Models\TaskSubmissionItem;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;

class TaskSubmissionController extends Controller
{
    private const TASK_DEFINITIONS = [
        'messages' => [
            'group' => 'messages',
            'group_label' => 'No. of Messages',
            'label' => 'No. of Messages',
            'mode' => 'messages',
        ],
        'refunds' => [
            'group' => 'refunds',
            'group_label' => 'Refunds',
            'label' => 'Refunds',
            'mode' => 'quantity',
            'unit' => 'count',
        ],
        'help_desk_request' => [
            'group' => 'help_desk_request',
            'group_label' => 'Help Desk Request',
            'label' => 'Help Desk Request',
            'mode' => 'quantity',
            'unit' => 'count',
        ],
        'review_follow_up' => [
            'group' => 'follow_ups',
            'group_label' => 'Follow Ups',
            'label' => 'Review Follow Up',
            'mode' => 'quantity',
            'unit' => 'count',
        ],
        'payment_link_follow_up' => [
            'group' => 'follow_ups',
            'group_label' => 'Follow Ups',
            'label' => 'Payment Link Follow Up',
            'mode' => 'quantity',
            'unit' => 'count',
        ],
        'total_unattended' => [
            'group' => 'unattended',
            'group_label' => 'Unattended',
            'label' => 'Total Unattended',
            'mode' => 'quantity',
            'unit' => 'count',
        ],
        'total_respondents' => [
            'group' => 'unattended',
            'group_label' => 'Unattended',
            'label' => 'Total Respondents',
            'mode' => 'quantity',
            'unit' => 'count',
        ],
        'rosters' => [
            'group' => 'rosters',
            'group_label' => 'Rosters',
            'label' => 'Rosters',
            'mode' => 'quantity',
            'unit' => 'minutes',
        ],
    ];

    public function index(Request $request): JsonResponse
    {
        $limit = min(max((int) $request->integer('limit', 6), 1), 50);
        $role = Str::lower((string) ($request->user()?->role ?? ''));
        $baseQuery = TaskSubmission::query();

        if (! in_array($role, ['admin', 'auditor'], true)) {
            $baseQuery->where('user_id', $request->user()?->id);
        } elseif ($request->filled('user_id')) {
            $baseQuery->where('user_id', $request->integer('user_id'));
        }

        if ($request->filled('start_date')) {
            $baseQuery->whereDate(DB::raw('COALESCE(start_at, submitted_at)'), '>=', $request->input('start_date'));
        }

        if ($request->filled('end_date')) {
            $baseQuery->whereDate(DB::raw('COALESCE(start_at, submitted_at)'), '<=', $request->input('end_date'));
        }

        if ($request->filled('task_key')) {
            $baseQuery->where('task_key', $request->input('task_key'));
        }

        // Consolidate historical duplicates so list views show one row
        // per user + task type + date, using the latest submission row.
        $latestIds = (clone $baseQuery)
            ->selectRaw('MAX(id) as id')
            ->groupBy('user_id', 'task_key', DB::raw('DATE(COALESCE(start_at, submitted_at))'));

        $query = TaskSubmission::query()
            ->with(['items', 'user:id,name,email,role,team'])
            ->whereIn('id', $latestIds)
            ->latest('submitted_at');

        $paginated = $query->paginate($limit);

        return response()->json([
            'data' => collect($paginated->items())
                ->map(fn (TaskSubmission $submission) => $this->submissionPayload($submission, $request))
                ->values(),
            'meta' => [
                'currentPage' => $paginated->currentPage(),
                'lastPage' => $paginated->lastPage(),
                'perPage' => $paginated->perPage(),
                'total' => $paginated->total(),
            ],
        ]);
    }

    public function store(Request $request): JsonResponse
    {
        $validator = Validator::make($request->all(), [
            'task_key' => ['required', 'string', Rule::in(array_keys(self::TASK_DEFINITIONS))],
            'start_at' => ['required', 'date'],
            'end_at' => ['required', 'date', 'after_or_equal:start_at'],
            'items' => ['required', 'array', 'min:1', 'max:30'],
            'items.*.account_number' => ['nullable', 'integer', 'min:1', 'max:30'],
            'items.*.entry_date' => ['nullable', 'date'],
            'items.*.entry_time' => ['nullable', 'date_format:H:i'],
            'items.*.description' => ['nullable', 'string', 'max:2000'],
            'items.*.quantity' => ['nullable', 'integer', 'min:0'],
            'items.*.quantity_screenshot' => ['nullable', 'file', 'image', 'max:5120'],
            'items.*.total_quantity' => ['nullable', 'integer', 'min:0'],
            'items.*.pending_quantity' => ['nullable', 'integer', 'min:0'],
            'items.*.done_quantity' => ['nullable', 'integer', 'min:0'],
        ]);

        $validator->after(function ($validator) use ($request) {
            $taskKey = (string) $request->input('task_key', '');
            $definition = self::TASK_DEFINITIONS[$taskKey] ?? null;

            if (! $definition) {
                return;
            }

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

            if (! is_array($items) || count($items) < 1) {
                $validator->errors()->add('items', 'At least one task item is required.');
                return;
            }

            if ($definition['mode'] === 'messages') {
                $accountNumbers = [];

                foreach ($items as $index => $item) {
                    $accountNumber = isset($item['account_number']) ? (int) $item['account_number'] : null;

                    if (! $accountNumber) {
                        $validator->errors()->add("items.$index.account_number", 'Select a valid account.');
                    } elseif (in_array($accountNumber, $accountNumbers, true)) {
                        $validator->errors()->add("items.$index.account_number", 'Each account can only be selected once.');
                    } else {
                        $accountNumbers[] = $accountNumber;
                    }

                    foreach (['total', 'pending', 'done'] as $metric) {
                        $quantity = $item["{$metric}_quantity"] ?? null;

                        if ($quantity === null || $quantity === '') {
                            $validator->errors()->add("items.$index.{$metric}_quantity", ucfirst($metric).' quantity is required.');
                        }
                    }

                }

                return;
            }

            if (count($items) !== 1) {
                $validator->errors()->add('items', 'Only one quantity row is allowed for this task.');
                return;
            }

            $firstItem = $items[0] ?? [];

            if (($firstItem['quantity'] ?? null) === null || ($firstItem['quantity'] ?? '') === '') {
                $validator->errors()->add('items.0.quantity', 'Quantity is required.');
            }

            if (empty($firstItem['description'])) {
                $validator->errors()->add('items.0.description', 'Description is required.');
            }

        });

        $payload = $validator->validate();
        $definition = self::TASK_DEFINITIONS[$payload['task_key']];

        $submission = DB::transaction(function () use ($payload, $definition, $request) {
            $startAt = \Carbon\Carbon::parse($payload['start_at']);
            $endAt = \Carbon\Carbon::parse($payload['end_at']);
            $targetDate = $startAt->toDateString();
            $submission = TaskSubmission::query()
                ->where('user_id', $request->user()->id)
                ->where('task_key', $payload['task_key'])
                ->whereDate(DB::raw('COALESCE(start_at, submitted_at)'), $targetDate)
                ->latest('id')
                ->first();

            if (! $submission) {
                $submission = TaskSubmission::query()->create([
                    'user_id' => $request->user()->id,
                    'task_group' => $definition['group'],
                    'task_group_label' => $definition['group_label'],
                    'task_key' => $payload['task_key'],
                    'task_label' => $definition['label'],
                    'is_audited' => false,
                    'start_at' => $startAt,
                    'end_at' => $endAt,
                    'submitted_at' => now(),
                ]);
            } else {
                // Keep one submission row per task type per day for each user.
                $submission->update([
                    'task_group' => $definition['group'],
                    'task_group_label' => $definition['group_label'],
                    'task_label' => $definition['label'],
                    'is_audited' => false,
                    'audited_at' => null,
                    'audited_by' => null,
                    'start_at' => $startAt,
                    'end_at' => $endAt,
                    'submitted_at' => now(),
                ]);

                Storage::disk('public')->deleteDirectory("task-submissions/{$submission->id}");
                $submission->items()->delete();
            }

            foreach ($payload['items'] as $index => $item) {
                if ($definition['mode'] === 'messages') {
                    $accountNumber = (int) $item['account_number'];
                    $quantityScreenshot = $this->storeScreenshot($request->file("items.$index.quantity_screenshot"), $submission, "account-{$accountNumber}-proof");

                    TaskSubmissionItem::query()->create([
                        'task_submission_id' => $submission->id,
                        'sort_order' => $index + 1,
                        'line_type' => 'account',
                        'line_label' => "Account {$accountNumber}",
                        'account_number' => $accountNumber,
                        'entry_date' => $item['entry_date'] ?? $startAt->toDateString(),
                        'entry_time' => $item['entry_time'] ?? $startAt->format('H:i'),
                        'description' => null,
                        'quantity_screenshot_path' => $quantityScreenshot['path'],
                        'quantity_screenshot_name' => $quantityScreenshot['name'],
                        'total_quantity' => (int) $item['total_quantity'],
                        'pending_quantity' => (int) $item['pending_quantity'],
                        'done_quantity' => (int) $item['done_quantity'],
                    ]);

                    continue;
                }

                $quantityScreenshot = $this->storeScreenshot($request->file("items.$index.quantity_screenshot"), $submission, "line-{$index}-quantity");

                TaskSubmissionItem::query()->create([
                    'task_submission_id' => $submission->id,
                    'sort_order' => $index + 1,
                    'line_type' => 'metric',
                    'line_label' => $definition['label'],
                    'entry_date' => $item['entry_date'] ?? $startAt->toDateString(),
                    'entry_time' => $item['entry_time'] ?? $startAt->format('H:i'),
                    'description' => isset($item['description']) ? trim((string) $item['description']) : null,
                    'quantity' => (int) $item['quantity'],
                    'quantity_unit' => $definition['unit'] ?? 'count',
                    'quantity_screenshot_path' => $quantityScreenshot['path'],
                    'quantity_screenshot_name' => $quantityScreenshot['name'],
                ]);
            }

            return $submission->load(['items', 'user:id,name,email,role,team']);
        });

        return response()->json([
            'message' => 'Task submitted successfully.',
            'submission' => $this->submissionPayload($submission, $request),
        ], 201);
    }

    public function update(Request $request, TaskSubmission $submission): JsonResponse
    {
        $role = Str::lower((string) ($request->user()?->role ?? ''));
        if (! in_array($role, ['admin', 'auditor'], true) && (int) $submission->user_id !== (int) $request->user()?->id) {
            return response()->json(['message' => 'Unauthorized.'], 403);
        }

        $validator = Validator::make($request->all(), [
            'is_audited' => ['nullable', 'boolean'],
            'start_at' => ['nullable', 'date'],
            'end_at' => ['nullable', 'date', 'after_or_equal:start_at'],
            'items' => ['nullable', 'array', 'min:1'],
            'items.*.id' => ['nullable', 'integer'],
            'items.*.entry_date' => ['nullable', 'date'],
            'items.*.entry_time' => ['nullable', 'date_format:H:i'],
            'items.*.description' => ['nullable', 'string', 'max:2000'],
            'items.*.quantity' => ['nullable', 'integer', 'min:0'],
            'items.*.total_quantity' => ['nullable', 'integer', 'min:0'],
            'items.*.pending_quantity' => ['nullable', 'integer', 'min:0'],
            'items.*.done_quantity' => ['nullable', 'integer', 'min:0'],
            'items.*.quantity_screenshot' => ['nullable', 'file', 'image', 'max:5120'],
        ]);

        $payload = $validator->validate();
        $itemsById = $submission->items->keyBy('id');

        DB::transaction(function () use ($payload, $itemsById, $request, $submission) {
            $submissionUpdates = [];
            if (array_key_exists('start_at', $payload)) {
                $submissionUpdates['start_at'] = $payload['start_at'];
            }
            if (array_key_exists('end_at', $payload)) {
                $submissionUpdates['end_at'] = $payload['end_at'];
            }

            if (array_key_exists('is_audited', $payload)) {
                $isAudited = (bool) $payload['is_audited'];
                $submissionUpdates = array_merge($submissionUpdates, [
                    'is_audited' => $isAudited,
                    'audited_at' => $isAudited ? now() : null,
                    'audited_by' => $isAudited ? $request->user()?->id : null,
                ]);
            }

            if ($submissionUpdates !== []) {
                $submission->update($submissionUpdates);
            }

            foreach (($payload['items'] ?? []) as $index => $itemPayload) {
                /** @var TaskSubmissionItem|null $item */
                $itemId = isset($itemPayload['id']) ? (int) $itemPayload['id'] : null;
                if (! $itemId) {
                    continue;
                }

                $item = $itemsById->get($itemId);
                if (! $item) {
                    continue;
                }

                $updates = [
                    'entry_date' => $itemPayload['entry_date'] ?? null,
                    'entry_time' => $itemPayload['entry_time'] ?? null,
                ];

                if ($item->line_type === 'account' && $item->total_quantity !== null) {
                    $updates['total_quantity'] = $itemPayload['total_quantity'] ?? $item->total_quantity;
                    $updates['pending_quantity'] = $itemPayload['pending_quantity'] ?? $item->pending_quantity;
                    $updates['done_quantity'] = $itemPayload['done_quantity'] ?? $item->done_quantity;
                } else {
                    $updates['quantity'] = $itemPayload['quantity'] ?? $item->quantity;
                    if (array_key_exists('description', $itemPayload)) {
                        $updates['description'] = trim((string) ($itemPayload['description'] ?? ''));
                    }
                }

                $quantityScreenshot = $this->storeScreenshot($request->file("items.$index.quantity_screenshot"), $submission, "item-{$item->id}-quantity");
                if ($quantityScreenshot['path']) {
                    $updates['quantity_screenshot_path'] = $quantityScreenshot['path'];
                    $updates['quantity_screenshot_name'] = $quantityScreenshot['name'];
                }

                $item->update($updates);
            }
        });

        $updated = $submission->fresh(['items', 'user:id,name,email,role,team']);

        return response()->json([
            'message' => 'Submission updated successfully.',
            'submission' => $this->submissionPayload($updated, $request),
        ]);
    }

    public function updateAuditStatus(Request $request, TaskSubmission $submission): JsonResponse
    {
        $role = Str::lower((string) ($request->user()?->role ?? ''));
        if (! in_array($role, ['admin', 'auditor'], true)) {
            return response()->json(['message' => 'Unauthorized.'], 403);
        }

        $payload = Validator::make($request->all(), [
            'is_audited' => ['required', 'boolean'],
        ])->validate();

        $isAudited = (bool) $payload['is_audited'];
        $submission->update([
            'is_audited' => $isAudited,
            'audited_at' => $isAudited ? now() : null,
            'audited_by' => $isAudited ? $request->user()?->id : null,
        ]);

        $updated = $submission->fresh(['items', 'user:id,name,email,role,team']);

        return response()->json([
            'message' => 'Audit status updated successfully.',
            'submission' => $this->submissionPayload($updated, $request),
        ]);
    }

    public function destroy(Request $request, TaskSubmission $submission): JsonResponse
    {
        $role = Str::lower((string) ($request->user()?->role ?? ''));
        if (! in_array($role, ['admin', 'auditor'], true) && (int) $submission->user_id !== (int) $request->user()?->id) {
            return response()->json(['message' => 'Unauthorized.'], 403);
        }

        DB::transaction(function () use ($submission) {
            $groupDate = ($submission->start_at ?? $submission->submitted_at)?->toDateString();

            $cohort = TaskSubmission::query()
                ->where('user_id', $submission->user_id)
                ->where('task_key', $submission->task_key)
                ->when(
                    $groupDate,
                    fn ($query) => $query->whereDate(DB::raw('COALESCE(start_at, submitted_at)'), $groupDate),
                    fn ($query) => $query->where('id', $submission->id)
                )
                ->get();

            foreach ($cohort as $submissionRow) {
                Storage::disk('public')->deleteDirectory("task-submissions/{$submissionRow->id}");
                $submissionRow->delete();
            }
        });

        return response()->json([
            'message' => 'Submission deleted successfully.',
        ]);
    }

    private function storeScreenshot(?UploadedFile $file, TaskSubmission $submission, string $slot): array
    {
        if (! $file) {
            return [
                'path' => null,
                'name' => null,
            ];
        }

        $extension = strtolower($file->getClientOriginalExtension() ?: $file->extension() ?: 'png');
        $filename = "{$slot}-".Str::uuid().".{$extension}";
        $path = $file->storeAs("task-submissions/{$submission->id}", $filename, 'public');

        return [
            'path' => $path,
            'name' => $file->getClientOriginalName(),
        ];
    }

    private function submissionPayload(TaskSubmission $submission, Request $request): array
    {
        return [
            'id' => $submission->id,
            'taskGroup' => $submission->task_group,
            'taskGroupLabel' => $submission->task_group_label,
            'taskKey' => $submission->task_key,
            'taskLabel' => $submission->task_label,
            'isAudited' => (bool) $submission->is_audited,
            'auditedAt' => $submission->audited_at?->format('Y-m-d h:i A'),
            'submittedAt' => $submission->submitted_at?->format('Y-m-d h:i A'),
            'startAt' => $submission->start_at?->format('Y-m-d H:i:s'),
            'endAt' => $submission->end_at?->format('Y-m-d H:i:s'),
            'itemCount' => $submission->items->count(),
            'user' => $submission->user?->only('id', 'name', 'email', 'role', 'team'),
            'items' => $submission->items
                ->sortBy('sort_order')
                ->map(fn (TaskSubmissionItem $item) => [
                    'id' => $item->id,
                    'lineType' => $item->line_type,
                    'lineLabel' => $item->line_label,
                    'accountNumber' => $item->account_number,
                    'entryDate' => $item->entry_date,
                    'entryTime' => $item->entry_time ? substr((string) $item->entry_time, 0, 5) : null,
                    'description' => $item->description,
                    'quantity' => $item->quantity,
                    'quantityUnit' => $item->quantity_unit,
                    'quantityScreenshotName' => $item->quantity_screenshot_name,
                    'quantityScreenshotUrl' => $this->screenshotUrl($item->quantity_screenshot_path, $request),
                    'totalQuantity' => $item->total_quantity,
                    'totalScreenshotName' => $item->total_screenshot_name,
                    'totalScreenshotUrl' => $this->screenshotUrl($item->total_screenshot_path, $request),
                    'pendingQuantity' => $item->pending_quantity,
                    'pendingScreenshotName' => $item->pending_screenshot_name,
                    'pendingScreenshotUrl' => $this->screenshotUrl($item->pending_screenshot_path, $request),
                    'doneQuantity' => $item->done_quantity,
                    'doneScreenshotName' => $item->done_screenshot_name,
                    'doneScreenshotUrl' => $this->screenshotUrl($item->done_screenshot_path, $request),
                ])
                ->values(),
        ];
    }

    private function screenshotUrl(?string $path, Request $request): ?string
    {
        if (! $path) {
            return null;
        }

        $url = Storage::disk('public')->url($path);
        $urlPath = parse_url($url, PHP_URL_PATH) ?: $url;

        if (! Str::startsWith($urlPath, '/')) {
            $urlPath = '/'.ltrim($urlPath, '/');
        }

        return rtrim($request->getSchemeAndHttpHost(), '/').$urlPath;
    }
}
