<?php

namespace App\Support\RightRail;

use App\Models\Activity;
use App\Models\Company;
use App\Models\Contact;
use App\Models\Deal;
use App\Models\Task;
use App\Models\User;
use App\Models\WebForm;
use App\Models\WebFormSubmission;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;

class NotificationProvider
{
    /**
     * @return Collection<int, array<string, mixed>>
     */
    public function notifications(User $user): Collection
    {
        $now = now();
        $viewedNotificationKeys = $this->viewedNotificationKeys($user);

        $taskAlerts = Task::query()
            ->where('assignee_id', $user->id)
            ->whereIn('status', ['todo', 'in_progress', 'review'])
            ->whereNotNull('due_at')
            ->where('due_at', '<', $now)
            ->orderBy('due_at')
            ->limit(6)
            ->get(['id', 'title', 'due_at'])
            ->map(fn (Task $task): array => [
                'key' => 'task-'.$task->id.'-'.($task->due_at?->timestamp ?? 0),
                'at' => $task->due_at,
                'icon' => 'fa-solid fa-triangle-exclamation',
                'title' => 'Overdue task',
                'subtitle' => '#'.$task->id.' · '.$task->title,
                'url' => route('tasks.show', $task),
            ]);

        $assignedTaskAlerts = Task::query()
            ->where('assignee_id', $user->id)
            ->whereIn('status', ['todo', 'in_progress', 'review'])
            ->where(function ($query) use ($user): void {
                $query->whereNull('creator_id')
                    ->orWhere('creator_id', '!=', $user->id);
            })
            ->where('created_at', '>=', $now->copy()->subDays(30))
            ->orderByDesc('created_at')
            ->limit(6)
            ->get(['id', 'title', 'created_at'])
            ->map(fn (Task $task): array => [
                'key' => 'task-'.$task->id.'-assigned-'.($task->created_at?->timestamp ?? 0),
                'at' => $task->created_at ?? $now,
                'icon' => 'fa-solid fa-list-check',
                'title' => 'New task assigned',
                'subtitle' => '#'.$task->id.' · '.$task->title,
                'url' => route('tasks.show', $task),
            ]);

        $dealAlerts = Deal::query()
            ->where('owner_id', $user->id)
            ->where('status', 'open')
            ->whereNotNull('expected_close_at')
            ->whereDate('expected_close_at', '<=', $now->copy()->addDay()->toDateString())
            ->orderBy('expected_close_at')
            ->limit(4)
            ->get(['id', 'title', 'amount', 'currency', 'expected_close_at'])
            ->map(fn (Deal $deal): array => [
                'key' => 'deal-'.$deal->id.'-'.($deal->expected_close_at?->copy()->startOfDay()->timestamp ?? 0),
                'at' => $deal->expected_close_at ? Carbon::parse($deal->expected_close_at)->startOfDay() : $now,
                'icon' => 'fa-solid fa-briefcase',
                'title' => 'The deal requires attention',
                'subtitle' => $deal->title.' · $'.number_format((float) $deal->amount, 0, '.', ' '),
                'url' => route('deals.show', $deal),
            ]);

        $activityAlerts = Activity::query()
            ->with('deal:id,title')
            ->where('user_id', $user->id)
            ->orderByDesc('occurred_at')
            ->limit(4)
            ->get(['id', 'deal_id', 'subject', 'occurred_at'])
            ->map(function (Activity $activity) use ($now): array {
                $deal = $activity->deal;

                return [
                    'key' => 'activity-'.$activity->id,
                    'at' => $activity->occurred_at ?? $now,
                    'icon' => 'fa-solid fa-bell',
                    'title' => 'New activity',
                    'subtitle' => $activity->subject,
                    'url' => $deal ? route('deals.show', $deal) : route('activities.show', $activity),
                ];
            });

        $webFormSubmissionAlerts = WebFormSubmission::query()
            ->with('form:id,name,owner_id')
            ->where('status', 'success')
            ->whereHas('form', fn ($query) => $query->where('owner_id', $user->id))
            ->orderByDesc('submitted_at')
            ->orderByDesc('id')
            ->limit(8)
            ->get(['id', 'web_form_id', 'result_type', 'result_id', 'submitted_at', 'created_at'])
            ->map(function (WebFormSubmission $submission) use ($now): array {
                return [
                    'key' => 'form-submission-'.$submission->id,
                    'at' => $submission->submitted_at ?? $submission->created_at ?? $now,
                    'icon' => 'fa-solid fa-rectangle-list',
                    'title' => __('Form submitted'),
                    'subtitle' => $this->formSubmissionSubtitle($submission),
                    'url' => $this->formSubmissionUrl($submission),
                ];
            });

        return $taskAlerts
            ->concat($assignedTaskAlerts)
            ->concat($dealAlerts)
            ->concat($activityAlerts)
            ->concat($webFormSubmissionAlerts)
            ->sortByDesc(fn (array $item) => $item['at'])
            ->reject(fn (array $item): bool => in_array((string) ($item['key'] ?? ''), $viewedNotificationKeys, true))
            ->values()
            ->take(10)
            ->map(function (array $item): array {
                $at = $item['at'] instanceof Carbon ? $item['at'] : Carbon::parse((string) $item['at']);
                unset($item['at']);
                $item['time'] = $at->diffForHumans();

                return $item;
            });
    }

    /**
     * @return list<string>
     */
    private function viewedNotificationKeys(User $user): array
    {
        return collect($user->right_rail_viewed_notifications ?? [])
            ->filter(fn (mixed $value): bool => is_string($value) && $value !== '')
            ->unique()
            ->values()
            ->all();
    }

    private function formSubmissionSubtitle(WebFormSubmission $submission): string
    {
        $formName = trim((string) ($submission->form?->name ?? __('Form')));
        if ($formName === '') {
            $formName = __('Form');
        }

        return $formName.' · '.$this->formSubmissionActionLabel((string) $submission->result_type, (int) $submission->result_id);
    }

    private function formSubmissionActionLabel(string $resultType, int $resultId): string
    {
        $suffix = $resultId > 0 ? ' #'.$resultId : '';

        return match ($resultType) {
            'task' => __('Created task').$suffix,
            'deal' => __('Created deal').$suffix,
            'company' => __('Created company').$suffix,
            'contact' => __('Created contact').$suffix,
            default => __('Submission processed'),
        };
    }

    private function formSubmissionUrl(WebFormSubmission $submission): string
    {
        $form = $submission->form;
        if (! $form instanceof WebForm) {
            return route('dashboard');
        }

        $resultType = (string) $submission->result_type;
        $resultId = (int) ($submission->result_id ?? 0);

        if ($resultId > 0) {
            if ($resultType === 'task' && Task::query()->whereKey($resultId)->exists()) {
                return route('tasks.show', ['task' => $resultId]);
            }

            if ($resultType === 'deal' && Deal::query()->whereKey($resultId)->exists()) {
                return route('deals.show', ['deal' => $resultId]);
            }

            if ($resultType === 'company' && Company::query()->whereKey($resultId)->exists()) {
                return route('companies.show', ['company' => $resultId]);
            }

            if ($resultType === 'contact' && Contact::query()->whereKey($resultId)->exists()) {
                return route('contacts.show', ['contact' => $resultId]);
            }
        }

        return route('forms.edit', $form);
    }
}
