<?php

namespace App\Http\Controllers\Api\Mobile;

use App\Http\Controllers\Controller;
use App\Models\Campaign;
use App\Models\Conversation;
use App\Models\Deal;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;

class InsightsController extends Controller
{
    /**
     * GET /api/v1/mobile/insights
     *
     * Mobile dashboard payload: KPI cards, recent conversations, hot deals,
     * and the latest campaign snapshot.
     */
    public function show(Request $request): JsonResponse
    {
        $data = Validator::make($request->query(), [
            'range' => ['sometimes', 'string', 'in:today,7d,30d,90d'],
            'recent_limit' => ['sometimes', 'integer', 'min:1', 'max:10'],
            'deal_limit' => ['sometimes', 'integer', 'min:1', 'max:10'],
        ])->validate();

        $workspaceId = $request->user()->active_workspace_id;
        $rangeKey = (string) ($data['range'] ?? '7d');
        $recentLimit = (int) ($data['recent_limit'] ?? 4);
        $dealLimit = (int) ($data['deal_limit'] ?? 4);

        [$currentStart, $currentEnd] = $this->dateBounds($rangeKey);
        [$previousStart, $previousEnd] = $this->previousBounds($currentStart, $currentEnd);

        if (! $workspaceId) {
            return response()->json($this->emptyPayload(
                $rangeKey,
                $currentStart,
                $currentEnd,
            ));
        }

        $openCurrent = Conversation::where('workspace_id', $workspaceId)
            ->whereIn('status', ['open', 'pending', 'snoozed'])
            ->whereBetween('last_message_at', [$currentStart, $currentEnd])
            ->count();

        $openPrevious = Conversation::where('workspace_id', $workspaceId)
            ->whereIn('status', ['open', 'pending', 'snoozed'])
            ->whereBetween('last_message_at', [$previousStart, $previousEnd])
            ->count();

        $resolvedCurrent = Conversation::where('workspace_id', $workspaceId)
            ->whereNotNull('resolved_at')
            ->whereBetween('resolved_at', [$currentStart, $currentEnd])
            ->count();

        $resolvedPrevious = Conversation::where('workspace_id', $workspaceId)
            ->whereNotNull('resolved_at')
            ->whereBetween('resolved_at', [$previousStart, $previousEnd])
            ->count();

        $replySecondsCurrent = $this->averageResponseSeconds($workspaceId, $currentStart, $currentEnd);
        $replySecondsPrevious = $this->averageResponseSeconds($workspaceId, $previousStart, $previousEnd);

        $pipelineCurrent = $this->pipelineValueAt($workspaceId, $currentEnd);
        $pipelinePrevious = $this->pipelineValueAt($workspaceId, $previousEnd);

        $recentConversations = Conversation::where('workspace_id', $workspaceId)
            ->whereNull('deleted_at')
            ->where('status', '!=', 'spam')
            ->with(['contact:id,first_name,last_name,email,avatar_path'])
            ->orderByDesc('last_message_at')
            ->orderByDesc('id')
            ->limit($recentLimit)
            ->get()
            ->map(function (Conversation $conversation) {
                $contact = $conversation->contact;

                return [
                    'id' => $conversation->id,
                    'subject' => $conversation->subject,
                    'preview' => $conversation->preview,
                    'channel' => $conversation->channel,
                    'channel_label' => strtoupper((string) $conversation->channel),
                    'status' => $conversation->status,
                    'is_unread' => ! (bool) $conversation->is_read,
                    'last_message_at' => $conversation->last_message_at?->toIso8601String(),
                    'last_message_human' => $this->shortDiffForHumans($conversation->last_message_at),
                    'contact' => $contact ? [
                        'id' => $contact->id,
                        'name' => $contact->full_name ?: $contact->email,
                        'email' => $contact->email,
                        'avatar_url' => $contact->avatar_path ? asset('storage/' . $contact->avatar_path) : null,
                        'initials' => $this->initials($contact->full_name ?: $contact->email ?: '?'),
                    ] : null,
                ];
            })
            ->values()
            ->all();

        $hotDeals = Deal::where('workspace_id', $workspaceId)
            ->where('status', 'open')
            ->with(['dealStage:id,name,win_probability', 'pipeline:id,name'])
            ->orderByDesc('value')
            ->orderBy('expected_close_date')
            ->orderByDesc('id')
            ->limit($dealLimit)
            ->get()
            ->map(function (Deal $deal) {
                $value = (float) $deal->value;

                return [
                    'id' => $deal->id,
                    'title' => $deal->title,
                    'value' => round($value, 2),
                    'currency' => strtoupper((string) ($deal->currency ?: 'USD')),
                    'value_formatted' => $this->formatCurrency($value, (string) ($deal->currency ?: 'USD')),
                    'stage' => [
                        'id' => $deal->dealStage?->id,
                        'name' => $deal->dealStage?->name,
                        'label' => strtoupper((string) ($deal->dealStage?->name ?? 'OPEN')),
                        'win_probability' => (int) ($deal->dealStage?->win_probability ?? 0),
                    ],
                    'pipeline' => $deal->pipeline ? [
                        'id' => $deal->pipeline->id,
                        'name' => $deal->pipeline->name,
                    ] : null,
                    'progress_percent' => (int) ($deal->dealStage?->win_probability ?? 0),
                    'expected_close_date' => $deal->expected_close_date?->toDateString(),
                    'expected_close_label' => $deal->expected_close_date
                        ? 'Closes ' . $deal->expected_close_date->format('M j')
                        : null,
                ];
            })
            ->values()
            ->all();

        $latestCampaign = Campaign::where('workspace_id', $workspaceId)
            ->whereIn('status', ['scheduled', 'sending', 'sent', 'completed'])
            ->orderByRaw('COALESCE(sent_at, scheduled_at, completed_at, created_at) DESC')
            ->orderByDesc('id')
            ->first();

        return response()->json([
            'period' => [
                'key' => $rangeKey,
                'label' => $this->rangeLabel($rangeKey),
                'from' => $currentStart->toIso8601String(),
                'to' => $currentEnd->toIso8601String(),
                'compare_label' => $this->comparisonLabel($rangeKey),
            ],
            'summary' => [
                'open_chats' => $this->countMetric($openCurrent, $openPrevious, $rangeKey),
                'resolved' => $this->countMetric($resolvedCurrent, $resolvedPrevious, $rangeKey),
                'avg_reply_time' => $this->durationMetric($replySecondsCurrent, $replySecondsPrevious, $rangeKey),
                'pipeline_value' => $this->currencyMetric($pipelineCurrent, $pipelinePrevious, $rangeKey),
            ],
            'recent_conversations' => $recentConversations,
            'hot_deals' => $hotDeals,
            'latest_campaign' => $latestCampaign ? $this->shapeCampaign($latestCampaign) : null,
        ]);
    }

    private function emptyPayload(
        string $rangeKey,
        Carbon $currentStart,
        Carbon $currentEnd,
    ): array {
        return [
            'period' => [
                'key' => $rangeKey,
                'label' => $this->rangeLabel($rangeKey),
                'from' => $currentStart->toIso8601String(),
                'to' => $currentEnd->toIso8601String(),
                'compare_label' => $this->comparisonLabel($rangeKey),
            ],
            'summary' => [
                'open_chats' => $this->countMetric(0, 0, $rangeKey),
                'resolved' => $this->countMetric(0, 0, $rangeKey),
                'avg_reply_time' => $this->durationMetric(null, null, $rangeKey),
                'pipeline_value' => $this->currencyMetric(0.0, 0.0, $rangeKey),
            ],
            'recent_conversations' => [],
            'hot_deals' => [],
            'latest_campaign' => null,
        ];
    }

    private function dateBounds(string $rangeKey): array
    {
        $end = now();

        $start = match ($rangeKey) {
            'today' => now()->startOfDay(),
            '30d' => now()->subDays(30)->startOfDay(),
            '90d' => now()->subDays(90)->startOfDay(),
            default => now()->subDays(7)->startOfDay(),
        };

        return [$start, $end];
    }

    private function previousBounds(Carbon $currentStart, Carbon $currentEnd): array
    {
        $durationSeconds = $currentStart->diffInSeconds($currentEnd) + 1;
        $previousEnd = $currentStart->copy()->subSecond();
        $previousStart = $previousEnd->copy()->subSeconds($durationSeconds - 1);

        return [$previousStart, $previousEnd];
    }

    private function averageResponseSeconds(int $workspaceId, Carbon $start, Carbon $end): ?float
    {
        $expression = $this->averageSecondsExpression('created_at', 'first_response_at');

        $row = Conversation::where('workspace_id', $workspaceId)
            ->whereNotNull('first_response_at')
            ->whereBetween('first_response_at', [$start, $end])
            ->selectRaw($expression . ' as avg_seconds')
            ->first();

        return isset($row?->avg_seconds) ? (float) $row->avg_seconds : null;
    }

    private function averageSecondsExpression(string $fromColumn, string $toColumn): string
    {
        return match (DB::connection()->getDriverName()) {
            'sqlite' => "AVG((julianday($toColumn) - julianday($fromColumn)) * 86400.0)",
            'pgsql' => "AVG(EXTRACT(EPOCH FROM ($toColumn - $fromColumn)))",
            default => "AVG(TIMESTAMPDIFF(SECOND, $fromColumn, $toColumn))",
        };
    }

    private function pipelineValueAt(int $workspaceId, Carbon $pointInTime): float
    {
        return (float) Deal::where('workspace_id', $workspaceId)
            ->where('created_at', '<=', $pointInTime)
            ->where(function ($query) use ($pointInTime) {
                $query->where('status', 'open')
                    ->orWhere(function ($won) use ($pointInTime) {
                        $won->whereNotNull('won_at')->where('won_at', '>', $pointInTime);
                    })
                    ->orWhere(function ($lost) use ($pointInTime) {
                        $lost->whereNotNull('lost_at')->where('lost_at', '>', $pointInTime);
                    });
            })
            ->sum('value');
    }

    private function countMetric(int $current, int $previous, string $rangeKey): array
    {
        $delta = $this->delta($current, $previous);

        return [
            'value' => $current,
            'formatted' => number_format($current),
            'delta_percent' => $delta['percent'],
            'delta_direction' => $delta['direction'],
            'delta_label' => $this->deltaLabel($delta, $rangeKey),
        ];
    }

    private function durationMetric(?float $currentSeconds, ?float $previousSeconds, string $rangeKey): array
    {
        $delta = $this->delta($currentSeconds, $previousSeconds);

        return [
            'value_seconds' => $currentSeconds !== null ? round($currentSeconds, 1) : null,
            'formatted' => $this->formatDuration($currentSeconds),
            'delta_percent' => $delta['percent'],
            'delta_direction' => $delta['direction'],
            'delta_label' => $this->deltaLabel($delta, $rangeKey),
        ];
    }

    private function currencyMetric(float $current, float $previous, string $rangeKey): array
    {
        $delta = $this->delta($current, $previous);

        return [
            'value' => round($current, 2),
            'currency' => 'USD',
            'formatted' => $this->formatCurrency($current, 'USD'),
            'delta_percent' => $delta['percent'],
            'delta_direction' => $delta['direction'],
            'delta_label' => $this->deltaLabel($delta, $rangeKey),
        ];
    }

    private function delta(int|float|null $current, int|float|null $previous): array
    {
        $currentValue = (float) ($current ?? 0);
        $previousValue = (float) ($previous ?? 0);
        $difference = $currentValue - $previousValue;

        if (abs($difference) < 0.00001) {
            return ['percent' => 0.0, 'direction' => 'flat'];
        }

        if (abs($previousValue) < 0.00001) {
            return ['percent' => 100.0, 'direction' => $difference > 0 ? 'up' : 'down'];
        }

        return [
            'percent' => round(abs(($difference / $previousValue) * 100), 1),
            'direction' => $difference > 0 ? 'up' : 'down',
        ];
    }

    private function deltaLabel(array $delta, string $rangeKey): string
    {
        $prefix = match ($delta['direction']) {
            'up' => '+',
            'down' => '-',
            default => '',
        };

        return $prefix . $delta['percent'] . '% ' . $this->comparisonLabel($rangeKey);
    }

    private function comparisonLabel(string $rangeKey): string
    {
        return match ($rangeKey) {
            'today' => 'vs yesterday',
            '30d' => 'vs previous 30 days',
            '90d' => 'vs previous 90 days',
            default => 'vs previous 7 days',
        };
    }

    private function rangeLabel(string $rangeKey): string
    {
        return match ($rangeKey) {
            'today' => 'Today',
            '30d' => 'Last 30 days',
            '90d' => 'Last 90 days',
            default => 'Last 7 days',
        };
    }

    private function shapeCampaign(Campaign $campaign): array
    {
        $sentAt = $campaign->sent_at ?? $campaign->completed_at ?? $campaign->scheduled_at ?? $campaign->created_at;
        $valueBase = max(
            (int) ($campaign->recipients_count ?? 0),
            (int) ($campaign->sent_count ?? 0),
            (int) (($campaign->delivered_count ?? 0) + ($campaign->bounced_count ?? 0))
        );

        return [
            'id' => $campaign->id,
            'name' => $campaign->name,
            'status' => $campaign->status,
            'status_label' => ucfirst((string) $campaign->status),
            'subject' => $campaign->subject,
            'sent_at' => $sentAt?->toIso8601String(),
            'sent_human' => $this->shortDiffForHumans($sentAt),
            'recipients_count' => (int) ($campaign->recipients_count ?? 0),
            'sent_count' => (int) ($campaign->sent_count ?? 0),
            'delivered_count' => (int) ($campaign->delivered_count ?? 0),
            'opened_count' => (int) ($campaign->opened_count ?? 0),
            'clicked_count' => (int) ($campaign->clicked_count ?? 0),
            'bounced_count' => (int) ($campaign->bounced_count ?? 0),
            'open_rate' => round((float) $campaign->open_rate, 1),
            'click_rate' => round((float) $campaign->click_rate, 1),
            'bounce_rate' => $valueBase > 0
                ? round(((int) ($campaign->bounced_count ?? 0) / $valueBase) * 100, 1)
                : 0.0,
        ];
    }

    private function formatDuration(?float $seconds): ?string
    {
        if ($seconds === null || $seconds <= 0) {
            return null;
        }

        if ($seconds < 60) {
            return round($seconds) . 's';
        }

        if ($seconds < 3600) {
            return round($seconds / 60) . 'm';
        }

        $hours = floor($seconds / 3600);
        $minutes = round(($seconds % 3600) / 60);

        if ($minutes <= 0) {
            return $hours . 'h';
        }

        return $hours . 'h ' . $minutes . 'm';
    }

    private function formatCurrency(float $amount, string $currency): string
    {
        $code = strtoupper($currency);
        $symbol = match ($code) {
            'EUR' => 'EUR ',
            'GBP' => 'GBP ',
            'INR' => 'INR ',
            default => '$',
        };

        if ($symbol === '$') {
            if (abs($amount) >= 1000) {
                return '$' . $this->compactNumber($amount);
            }

            return '$' . number_format($amount, 2);
        }

        return $symbol . number_format($amount, 2);
    }

    private function compactNumber(float $value): string
    {
        $abs = abs($value);

        if ($abs >= 1000000) {
            return number_format($value / 1000000, 1) . 'M';
        }

        if ($abs >= 1000) {
            return number_format($value / 1000, 1) . 'k';
        }

        return number_format($value, 0);
    }

    private function shortDiffForHumans(?Carbon $timestamp): ?string
    {
        if (! $timestamp) {
            return null;
        }

        $diffSeconds = abs($timestamp->diffInSeconds(now()));

        if ($diffSeconds < 60) {
            return max(1, $diffSeconds) . 's';
        }

        if ($diffSeconds < 3600) {
            return floor($diffSeconds / 60) . 'm';
        }

        if ($diffSeconds < 86400) {
            return floor($diffSeconds / 3600) . 'h';
        }

        return floor($diffSeconds / 86400) . 'd';
    }

    private function initials(?string $value): string
    {
        if (! $value) {
            return '?';
        }

        $parts = preg_split('/\s+/', trim($value));

        return strtoupper(substr($parts[0] ?? '', 0, 1) . substr($parts[1] ?? '', 0, 1)) ?: '?';
    }
}
