<?php

namespace App\Domains\Tasks\Actions;

use App\Domains\Contacts\Models\Contact;
use App\Domains\Conversations\Models\Conversation;
use App\Domains\Conversations\Models\Message;
use App\Domains\Orders\Models\Order;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Collection;

class BuildFollowUpCampaignAudienceAction
{
    /**
     * @return array{recipients: array<int, array<string, mixed>>, applied: array<string, mixed>}
     */
    public function execute(User $user, array $filters): array
    {
        $tenantId = $user->tenant_id;
        $conditions = collect($filters['conditions'] ?? [])
            ->filter(fn ($value) => in_array($value, [
                'inactive_90_days',
                'asked_no_purchase_7_days',
                'unread_no_reply_24h',
            ], true))
            ->values()
            ->all();

        $channel = (string) ($filters['channel'] ?? 'all');
        $inactiveDays = max(30, min(365, (int) ($filters['inactive_days'] ?? 90)));
        $noPurchaseDays = max(1, min(90, (int) ($filters['no_purchase_days'] ?? 7)));
        $unreadHours = max(1, min(72, (int) ($filters['unread_hours'] ?? 24)));
        $limit = max(1, min(500, (int) ($filters['limit'] ?? 200)));
        $cooldownHours = max(0, min(720, (int) ($filters['cooldown_hours'] ?? 72)));
        $maxTouches30Days = max(0, min(20, (int) ($filters['max_touches_30_days'] ?? 3)));

        $contacts = Contact::query()
            ->where('tenant_id', $tenantId)
            ->with([
                'conversations' => function ($query): void {
                    $query->select([
                        'id',
                        'tenant_id',
                        'contact_id',
                        'channel_type',
                        'status',
                        'unread_count',
                        'last_message_at',
                        'created_at',
                    ])
                    ->with([
                        'messages' => function ($messageQuery): void {
                            $messageQuery
                                ->select(['id', 'conversation_id', 'direction', 'body_text', 'created_at'])
                                ->where('direction', 'inbound')
                                ->orderByDesc('created_at')
                                ->limit(40);
                        },
                    ]);
                },
            ])
            ->get();

        /** @var array<string, string> $latestOrderAtByContact */
        $latestOrderAtByContact = Order::query()
            ->where('tenant_id', $tenantId)
            ->selectRaw('contact_id, MAX(created_at) as last_order_at')
            ->groupBy('contact_id')
            ->pluck('last_order_at', 'contact_id')
            ->all();

        $now = now();
        $audience = [];

        foreach ($contacts as $contact) {
            $conversations = collect($contact->conversations);
            if ($channel !== 'all') {
                $conversations = $conversations->where('channel_type', $channel)->values();
            }
            if ($conversations->isEmpty()) {
                continue;
            }

            /** @var Conversation|null $latestConversation */
            $latestConversation = $conversations->sortByDesc(fn (Conversation $conversation) => $conversation->last_message_at ?? $conversation->created_at)->first();
            if (! $latestConversation) {
                continue;
            }

            $lastInteractionAt = $latestConversation->last_message_at ?: $latestConversation->created_at;
            $lastOrderAtRaw = $latestOrderAtByContact[$contact->id] ?? null;
            $lastOrderAt = $lastOrderAtRaw ? Carbon::parse($lastOrderAtRaw) : null;
            $reasons = [];

            if ($cooldownHours > 0) {
                $hasRecentOutbound = Message::query()
                    ->where('tenant_id', $tenantId)
                    ->whereIn('conversation_id', $conversations->pluck('id')->all())
                    ->where('direction', 'outbound')
                    ->where('created_at', '>=', $now->copy()->subHours($cooldownHours))
                    ->exists();
                if ($hasRecentOutbound) {
                    continue;
                }
            }

            if ($maxTouches30Days > 0) {
                $touchCount = Message::query()
                    ->where('tenant_id', $tenantId)
                    ->whereIn('conversation_id', $conversations->pluck('id')->all())
                    ->where('direction', 'outbound')
                    ->where('created_at', '>=', $now->copy()->subDays(30))
                    ->count();
                if ($touchCount >= $maxTouches30Days) {
                    continue;
                }
            }

            if (in_array('inactive_90_days', $conditions, true) && $lastInteractionAt && $lastInteractionAt->lte($now->copy()->subDays($inactiveDays))) {
                $reasons[] = "No conversation activity for {$inactiveDays}+ days";
            }

            if (in_array('asked_no_purchase_7_days', $conditions, true)) {
                $inboundCandidate = $this->latestInboundAskOlderThan($conversations, $now->copy()->subDays($noPurchaseDays));
                if ($inboundCandidate && (! $lastOrderAt || $lastOrderAt->lt($inboundCandidate->created_at))) {
                    $reasons[] = "Asked about product but no order after {$noPurchaseDays}+ days";
                }
            }

            if (in_array('unread_no_reply_24h', $conditions, true)) {
                $hasUnreadStale = $conversations->contains(function (Conversation $conversation) use ($now, $unreadHours): bool {
                    if (($conversation->unread_count ?? 0) <= 0) {
                        return false;
                    }

                    $lastAt = $conversation->last_message_at ?: $conversation->created_at;

                    return $lastAt ? $lastAt->lte($now->copy()->subHours($unreadHours)) : false;
                });

                if ($hasUnreadStale) {
                    $reasons[] = "Unread conversation older than {$unreadHours} hours";
                }
            }

            if (empty($reasons)) {
                continue;
            }

            $audience[] = [
                'contactId' => $contact->id,
                'name' => $contact->primary_name,
                'phone' => $contact->whatsapp_phone,
                'instagramHandle' => $contact->instagram_handle,
                'channel' => $latestConversation->channel_type,
                'conversationId' => $latestConversation->id,
                'lastInteractionAt' => $lastInteractionAt?->toIso8601String(),
                'lastOrderAt' => $lastOrderAt?->toIso8601String(),
                'reasons' => $reasons,
            ];
        }

        $sorted = collect($audience)
            ->sortBy(fn (array $row) => $row['lastInteractionAt'] ?? '')
            ->take($limit)
            ->values()
            ->all();

        return [
            'recipients' => $sorted,
            'applied' => [
                'conditions' => $conditions,
                'channel' => $channel,
                'inactive_days' => $inactiveDays,
                'no_purchase_days' => $noPurchaseDays,
                'unread_hours' => $unreadHours,
                'cooldown_hours' => $cooldownHours,
                'max_touches_30_days' => $maxTouches30Days,
                'limit' => $limit,
            ],
        ];
    }

    /**
     * @param Collection<int, Conversation> $conversations
     */
    private function latestInboundAskOlderThan(Collection $conversations, Carbon $threshold): ?Message
    {
        /** @var Collection<int, Message> $messages */
        $messages = $conversations
            ->flatMap(fn (Conversation $conversation) => $conversation->messages ?? collect())
            ->filter(function (Message $message) use ($threshold): bool {
                if (! $message->created_at || $message->created_at->gt($threshold)) {
                    return false;
                }

                return $this->looksLikeBuyerIntent((string) $message->body_text);
            })
            ->sortByDesc('created_at')
            ->values();

        return $messages->first();
    }

    private function looksLikeBuyerIntent(string $text): bool
    {
        $normalized = mb_strtolower(trim($text));
        if ($normalized === '') {
            return false;
        }

        return (bool) preg_match('/(price|how much|available|delivery|deliver|order|buy|need|want|discount|offer|promo|cash on delivery|whish|\?)/i', $normalized);
    }
}
