<?php

namespace App\Livewire\Dashboard;

use App\Models\AiConfig;
use App\Models\Campaign;
use App\Models\ChannelIntegration;
use App\Models\Contact;
use App\Models\Conversation;
use App\Models\EmailAccount;
use App\Models\KbDocument;
use App\Models\Message;
use App\Models\Subscription;
use App\Models\UsageRecord;
use App\Traits\AuthorizesWorkspaceActions;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Livewire\Component;

class DashboardStats extends Component
{
    use AuthorizesWorkspaceActions;

    /**
     * Time-window filter for the four stat cards.
     *
     * Accepted: 'today', '7d', '30d', 'all'. Anything else falls back to '7d'
     * in resolveRange() — covers garbage from query-string tampering without
     * a try/catch.
     *
     * The sparkline always shows the last 7 daily bars regardless of $range —
     * its job is "shape of recent activity," not "totals for the selected
     * period," and forcing the bar count to follow $range would make the
     * "today" view a single bar.
     */
    public string $range = '7d';

    public function mount(): void
    {
        if (! $this->authorizeWorkspaceAction('view')) {
            return;
        }
    }

    public function setRange(string $range): void
    {
        $this->range = in_array($range, ['today', '7d', '30d', 'all'], true) ? $range : '7d';
    }

    /**
     * Return [startCarbon|null, humanLabel] for the current $range.
     * null start = no date filter (all-time).
     */
    protected function resolveRange(): array
    {
        return match ($this->range) {
            'today' => [now()->startOfDay(),       __('today')],
            '30d'   => [now()->subDays(30),        __('last 30 days')],
            'all'   => [null,                       __('all time')],
            default => [now()->subDays(7),         __('last 7 days')],
        };
    }

    public function placeholder()
    {
        return <<<'HTML'
        <div class="space-y-6 animate-pulse">
            <div class="h-32 bg-gray-200 rounded-2xl"></div>
            <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
                <div class="h-32 bg-gray-200 rounded-2xl"></div>
                <div class="h-32 bg-gray-200 rounded-2xl"></div>
                <div class="h-32 bg-gray-200 rounded-2xl"></div>
                <div class="h-32 bg-gray-200 rounded-2xl"></div>
            </div>
            <div class="h-64 bg-gray-200 rounded-2xl"></div>
        </div>
        HTML;
    }

    public function render()
    {
        $workspaceId = auth()->user()->active_workspace_id;

        [$rangeStart, $rangeLabel] = $this->resolveRange();
        // Cache keys include the range so flipping Today/7d/30d/All doesn't
        // serve stale numbers across windows.
        $cacheSuffix = $this->range;

        // Core stats — same four numbers as before, now date-filtered. We
        // build each query with an optional WHERE created_at >= $rangeStart
        // (null = all-time, no filter).
        //
        // IMPORTANT: every `conversations` query must filter out soft-deleted
        // rows. The model uses SoftDeletes so deleted rows just have
        // `deleted_at` set, but DB::table() bypasses the Eloquent scope and
        // would otherwise count trashed conversations toward "Total
        // Conversations" — leaving customers wondering why the dashboard
        // doesn't drop after they empty the Bin.
        $coreStats = Cache::remember("dashboard:core-stats:{$workspaceId}:{$cacheSuffix}", 30, function () use ($workspaceId, $rangeStart) {
            $convoQ = DB::table('conversations')
                ->where('workspace_id', $workspaceId)
                ->whereNull('deleted_at');
            if ($rangeStart) {
                $convoQ->where('created_at', '>=', $rangeStart);
            }
            $stats = (clone $convoQ)
                ->selectRaw('COUNT(*) as conversation_count')
                ->selectRaw('AVG(CASE WHEN first_response_at IS NOT NULL THEN TIMESTAMPDIFF(MINUTE, created_at, first_response_at) END) as avg_time')
                ->first();

            $aiRepliesQ = Message::where('workspace_id', $workspaceId)->where('sender_type', 'ai');
            if ($rangeStart) {
                $aiRepliesQ->where('created_at', '>=', $rangeStart);
            }

            $contactsQ = Contact::where('workspace_id', $workspaceId);
            if ($rangeStart) {
                $contactsQ->where('created_at', '>=', $rangeStart);
            }

            return [
                'conversation_count' => $stats->conversation_count ?? 0,
                'avg_time' => $stats->avg_time,
                'ai_replies_count' => $aiRepliesQ->count(),
                'contact_count' => $contactsQ->count(),
            ];
        });

        $conversationCount = $coreStats['conversation_count'];
        $avgResponseTime = $coreStats['avg_time'];
        $aiRepliesCount = $coreStats['ai_replies_count'];
        $contactCount = $coreStats['contact_count'];

        if ($avgResponseTime !== null) {
            if ($avgResponseTime < 60) {
                $avgResponseTimeDisplay = round($avgResponseTime) . 'm';
            } else {
                $avgResponseTimeDisplay = round($avgResponseTime / 60, 1) . 'h';
            }
        } else {
            $avgResponseTimeDisplay = '—';
        }

        // Recent conversations (real data, not hardcoded)
        $recentConversations = $workspaceId
            ? Conversation::where('workspace_id', $workspaceId)
                ->with('contact')
                ->latest('last_message_at')
                ->limit(5)
                ->get()
            : collect();

        // Per-metric 7-day sparklines.
        //
        // Previously a single $sparklineHeights — built from the conversation
        // count trend — was reused inside every stat card, so all four mini-
        // graphs were identical. Now each card gets its own daily-count
        // trend so the lines reflect what the card actually measures.
        //
        // The window is always 7 days regardless of the active $range — the
        // sparkline communicates "shape of recent activity" and forcing
        // single-bar charts for the "today" range would be useless.
        $sparkSince = now()->subDays(7);

        $convoTrend = DB::table('conversations')
            ->where('workspace_id', $workspaceId)
            ->whereNull('deleted_at') // exclude soft-deleted (Bin contents)
            ->where('created_at', '>=', $sparkSince)
            ->selectRaw('DATE(created_at) as d, COUNT(*) as c')
            ->groupBy('d')->orderBy('d')->pluck('c', 'd')->all();

        $aiReplyTrend = DB::table('messages')
            ->where('workspace_id', $workspaceId)
            ->where('sender_type', 'ai')
            ->where('created_at', '>=', $sparkSince)
            ->selectRaw('DATE(created_at) as d, COUNT(*) as c')
            ->groupBy('d')->orderBy('d')->pluck('c', 'd')->all();

        // "Avg response time" daily series — average minutes per day, treated
        // like a count for the bar normalisation below (zero days = empty bar).
        $avgTimeTrend = DB::table('conversations')
            ->where('workspace_id', $workspaceId)
            ->whereNull('deleted_at') // exclude soft-deleted
            ->whereNotNull('first_response_at')
            ->where('created_at', '>=', $sparkSince)
            ->selectRaw('DATE(created_at) as d, AVG(TIMESTAMPDIFF(MINUTE, created_at, first_response_at)) as c')
            ->groupBy('d')->orderBy('d')->pluck('c', 'd')->all();

        $contactsTrend = DB::table('contacts')
            ->where('workspace_id', $workspaceId)
            ->where('created_at', '>=', $sparkSince)
            ->selectRaw('DATE(created_at) as d, COUNT(*) as c')
            ->groupBy('d')->orderBy('d')->pluck('c', 'd')->all();

        // Build a fixed 7-day x-axis and project each metric onto it. Days
        // with no rows stay at zero. Heights are normalised per-metric so a
        // metric with small numbers still shows visible bars.
        $axis = [];
        for ($i = 6; $i >= 0; $i--) {
            $axis[] = now()->subDays($i)->format('Y-m-d');
        }
        $heightsFor = function (array $series) use ($axis): array {
            $values = array_map(fn ($d) => (float) ($series[$d] ?? 0), $axis);
            $max = max($values);
            if ($max <= 0) {
                return array_fill(0, 7, 10); // flat min-height bars
            }
            return array_map(fn ($v) => max(10, (int) round(($v / $max) * 100)), $values);
        };

        $sparklines = [
            'conversations' => $heightsFor($convoTrend),
            'ai_replies'    => $heightsFor($aiReplyTrend),
            'avg_time'      => $heightsFor($avgTimeTrend),
            'contacts'      => $heightsFor($contactsTrend),
        ];

        // Getting started checklist — cache for 5 minutes to avoid hitting DB every render
        $hasWorkspace = auth()->user()->active_workspace_id !== null;
        $checklist = $workspaceId ? cache()->remember("dashboard:checklist:{$workspaceId}", 300, function () use ($workspaceId) {
            return [
                'hasEmailAccount' => EmailAccount::where('workspace_id', $workspaceId)->exists(),
                'hasKbDocs' => KbDocument::where('workspace_id', $workspaceId)->exists(),
                'hasSentAiReply' => Message::where('workspace_id', $workspaceId)->where('sender_type', 'ai')->exists(),
                'hasAiConfig' => AiConfig::where('workspace_id', $workspaceId)->exists(),
                'hasChannels' => ChannelIntegration::where('workspace_id', $workspaceId)->where('status', 'active')->exists(),
                'hasTeamMembers' => DB::table('workspace_members')->where('workspace_id', $workspaceId)->count() > 1,
                // Per-channel active list so the Connect Channels card can
                // show a green check next to each one that's already wired up
                // instead of an undifferentiated "+" on every row.
                'activeChannels' => ChannelIntegration::where('workspace_id', $workspaceId)
                    ->where('status', 'active')
                    ->pluck('channel')
                    ->map(fn ($c) => strtolower((string) $c))
                    ->all(),
            ];
        }) : [];
        $hasEmailAccount = $checklist['hasEmailAccount'] ?? false;
        $hasKbDocs = $checklist['hasKbDocs'] ?? false;
        $hasSentAiReply = $checklist['hasSentAiReply'] ?? false;
        $hasAiConfig = $checklist['hasAiConfig'] ?? false;
        $hasChannels = $checklist['hasChannels'] ?? false;
        $hasTeamMembers = $checklist['hasTeamMembers'] ?? false;
        $activeChannels = $checklist['activeChannels'] ?? [];

        // Campaign metrics block — only computed (and rendered) for workspaces
        // that actually use campaigns, so a non-marketing user doesn't get a
        // wall of zeros. Joins campaign_recipients → campaigns for the
        // workspace filter since recipients have no workspace_id column.
        $hasCampaigns = $workspaceId
            ? Campaign::where('workspace_id', $workspaceId)->exists()
            : false;

        $campaignStats = [
            'sent_count' => 0,
            'opened_count' => 0,
            'clicked_count' => 0,
            'open_rate' => 0,
            'click_rate' => 0,
            'active_campaigns' => 0,
        ];

        if ($hasCampaigns) {
            $row = DB::table('campaign_recipients')
                ->join('campaigns', 'campaigns.id', '=', 'campaign_recipients.campaign_id')
                ->where('campaigns.workspace_id', $workspaceId)
                ->when($rangeStart, fn ($q) => $q->where('campaign_recipients.sent_at', '>=', $rangeStart))
                ->selectRaw('COUNT(CASE WHEN campaign_recipients.sent_at IS NOT NULL THEN 1 END) as sent_count')
                ->selectRaw('COUNT(CASE WHEN campaign_recipients.opened_at IS NOT NULL THEN 1 END) as opened_count')
                ->selectRaw('COUNT(CASE WHEN campaign_recipients.clicked_at IS NOT NULL THEN 1 END) as clicked_count')
                ->first();

            $sent = (int) ($row->sent_count ?? 0);
            $opened = (int) ($row->opened_count ?? 0);
            $clicked = (int) ($row->clicked_count ?? 0);

            $campaignStats = [
                'sent_count' => $sent,
                'opened_count' => $opened,
                'clicked_count' => $clicked,
                'open_rate'  => $sent > 0 ? round(($opened / $sent) * 100, 1)  : 0,
                'click_rate' => $sent > 0 ? round(($clicked / $sent) * 100, 1) : 0,
                'active_campaigns' => Campaign::where('workspace_id', $workspaceId)
                    ->whereIn('status', ['sending', 'scheduled'])
                    ->count(),
            ];
        }

        // AI spending cap warning
        $aiConfig = AiConfig::where('workspace_id', $workspaceId)->first();
        $aiCostLimit = $aiConfig->monthly_cost_limit ?? 0;
        $aiMonthlyCost = 0;
        $aiCostPercent = 0;
        if ($aiCostLimit > 0) {
            $aiMonthlyCost = (float) DB::table('ai_usage_logs')
                ->where('workspace_id', $workspaceId)
                ->whereRaw('DATE_FORMAT(created_at, "%Y-%m") = ?', [now()->format('Y-m')])
                ->sum('cost');
            $aiCostPercent = round(($aiMonthlyCost / $aiCostLimit) * 100);
        }

        // Plan usage warning — handle unlimited (free) plans correctly
        $subscription = Subscription::where('workspace_id', $workspaceId)
            ->where('status', 'active')
            ->with('plan.planFeatures')
            ->first();
        $aiRepliesLimit = $subscription?->plan?->featureLimit('ai_replies') ?? 50;
        $currentPeriod = now()->format('Y-m');
        $aiRepliesUsed = UsageRecord::where('workspace_id', $workspaceId)
            ->where('feature_key', 'ai_replies')
            ->where('period', $currentPeriod)
            ->sum('quantity');

        // Handle unlimited plans: if limit is 0 or null (unlimited), show "Unlimited"
        if ($aiRepliesLimit <= 0) {
            $usagePercent = 0;
            $aiRepliesLimitDisplay = 'Unlimited';
        } else {
            $usagePercent = round(($aiRepliesUsed / $aiRepliesLimit) * 100);
            $aiRepliesLimitDisplay = (string) $aiRepliesLimit;
        }

        return view('livewire.dashboard.dashboard-stats', [
            'conversationCount' => $conversationCount,
            'aiRepliesCount' => $aiRepliesCount,
            'contactCount' => $contactCount,
            'avgResponseTimeDisplay' => $avgResponseTimeDisplay,
            'sparklines' => $sparklines,
            'rangeLabel' => $rangeLabel,
            'range' => $this->range,
            'recentConversations' => $recentConversations,
            'hasWorkspace' => $hasWorkspace,
            'hasEmailAccount' => $hasEmailAccount,
            'hasKbDocs' => $hasKbDocs,
            'hasSentAiReply' => $hasSentAiReply,
            'hasAiConfig' => $hasAiConfig,
            'hasChannels' => $hasChannels,
            'hasTeamMembers' => $hasTeamMembers,
            'activeChannels' => $activeChannels,
            'aiRepliesUsed' => $aiRepliesUsed,
            'aiRepliesLimit' => $aiRepliesLimit,
            'aiRepliesLimitDisplay' => $aiRepliesLimitDisplay,
            'usagePercent' => $usagePercent,
            'aiCostLimit' => $aiCostLimit,
            'aiMonthlyCost' => $aiMonthlyCost,
            'aiCostPercent' => $aiCostPercent,
            'hasCampaigns' => $hasCampaigns,
            'campaignStats' => $campaignStats,
        ]);
    }
}
