<?php

namespace App\Http\Controllers\Dashboard;

use App\Http\Controllers\Api\V1\EmojiApiController;
use App\Http\Controllers\Controller;
use App\Models\Order;
use App\Models\Payment;
use App\Models\Subscription;
use App\Models\User;
use App\Models\UserApiKey;
use App\Models\UserKeyword;
use App\Models\WebhookEvent;
use App\Services\Auth\ApiKeyService;
use App\Services\Keywords\KeywordQuotaService;
use Carbon\Carbon;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Symfony\Component\HttpFoundation\BinaryFileResponse;

class UserDashboardController extends Controller
{
    public function __construct(
        private readonly ApiKeyService $keys,
        private readonly KeywordQuotaService $keywordQuota
    ) {}

    public function overview(Request $request): View
    {
        $user = $request->user();
        if (Gate::allows('admin')) {
            $days = 7;
            $start = now()->subDays($days - 1)->startOfDay();
            $labels = [];
            $values = [];
            $subsValues = [];
            $webhookValues = [];

            $rawUsers = DB::table('users')
                ->selectRaw('DATE(created_at) as day, COUNT(*) as total')
                ->where('created_at', '>=', $start)
                ->groupBy('day')
                ->orderBy('day')
                ->get()
                ->keyBy('day');

            $rawSubs = DB::table('subscriptions')
                ->selectRaw('DATE(created_at) as day, COUNT(*) as total')
                ->where('created_at', '>=', $start)
                ->groupBy('day')
                ->orderBy('day')
                ->get()
                ->keyBy('day');

            $rawWebhooks = DB::table('webhook_events')
                ->selectRaw('DATE(COALESCE(received_at, created_at)) as day, COUNT(*) as total')
                ->where('created_at', '>=', $start)
                ->groupBy('day')
                ->orderBy('day')
                ->get()
                ->keyBy('day');

            for ($i = $days - 1; $i >= 0; $i--) {
                $date = Carbon::now()->subDays($i)->format('Y-m-d');
                $labels[] = Carbon::parse($date)->format('M d');
                $values[] = (int) ($rawUsers[$date]->total ?? 0);
                $subsValues[] = (int) ($rawSubs[$date]->total ?? 0);
                $webhookValues[] = (int) ($rawWebhooks[$date]->total ?? 0);
            }

            $usersTotal = User::count();
            $usersPersonal = User::where('tier', 'personal')->count();
            $subscriptionsActive = Subscription::where('status', 'active')->count();
            $subscriptionsTotal = Subscription::count();
            $webhookTotal = WebhookEvent::count();
            $webhookErrors = WebhookEvent::where('status', 'error')->count();

            return view('dashboard.index', [
                'chartLabels' => $labels,
                'chartValues' => $values,
                'chartSubs' => $subsValues,
                'chartWebhooks' => $webhookValues,
                'overviewMetrics' => [
                    'users_total' => $usersTotal,
                    'users_personal' => $usersPersonal,
                    'subscriptions_active' => $subscriptionsActive,
                    'subscriptions_total' => $subscriptionsTotal,
                    'webhook_total' => $webhookTotal,
                    'webhook_errors' => $webhookErrors,
                ],
            ]);
        }

        $recentKeywords = UserKeyword::where('user_id', $user?->id)
            ->orderByDesc('id')
            ->limit(6)
            ->get();

        $recentWeekCount = UserKeyword::where('user_id', $user?->id)
            ->where('created_at', '>=', now()->subDays(7))
            ->count();

        $totalKeywords = UserKeyword::where('user_id', $user?->id)->count();
        $apiKeyCount = UserApiKey::where('user_id', $user?->id)
            ->whereNull('revoked_at')
            ->count();

        $activeSubscription = Subscription::where('user_id', $user?->id)
            ->orderByDesc('started_at')
            ->first();

        return view('dashboard.user.overview', [
            'totalKeywords' => $totalKeywords,
            'recentKeywords' => $recentKeywords,
            'recentWeekCount' => $recentWeekCount,
            'apiKeyCount' => $apiKeyCount,
            'subscription' => $activeSubscription,
        ]);
    }

    public function keywords(Request $request): View
    {
        $user = $request->user();

        $items = UserKeyword::where('user_id', $user?->id)
            ->orderByDesc('id')
            ->get();
        $activeCount = UserKeyword::where('user_id', $user?->id)
            ->where('is_active', true)
            ->count();

        $emojiLookup = [];
        $dataPath = (string) config('dewemoji.data_path');
        if (is_file($dataPath)) {
            $raw = file_get_contents($dataPath);
            $decoded = json_decode((string) $raw, true);
            if (is_array($decoded)) {
                foreach ($decoded['emojis'] ?? [] as $row) {
                    $slug = (string) ($row['slug'] ?? '');
                    if ($slug !== '') {
                        $emojiLookup[$slug] = [
                            'emoji' => (string) ($row['emoji'] ?? ''),
                            'name' => (string) ($row['name'] ?? $slug),
                        ];
                    }
                }
            }
        }

        return view('dashboard.user.keywords', [
            'items' => $items,
            'user' => $user,
            'emojiLookup' => $emojiLookup,
            'freeLimit' => $this->keywordLimitFor($user),
            'activeCount' => $activeCount,
        ]);
    }

    public function keywordSearch(Request $request, EmojiApiController $emoji): JsonResponse
    {
        $request->query->set('private', 'true');

        return $emoji->search($request);
    }

    public function storeKeyword(Request $request): RedirectResponse|JsonResponse
    {
        $user = $request->user();
        if (! $user) {
            abort(403);
        }

        $data = $request->validate([
            'emoji_slug' => 'required|string|max:120',
            'keyword' => 'required|string|max:200',
            'lang' => 'nullable|string|max:10',
        ]);

        $existing = UserKeyword::where('user_id', $user->id)
            ->where('emoji_slug', $data['emoji_slug'])
            ->where('keyword', $data['keyword'])
            ->first();
        $targetActive = $existing ? (bool) $existing->is_active : true;

        if ($limit = $this->keywordLimitFor($user)) {
            if (! $existing && $targetActive) {
                $activeCount = $this->keywordQuota->activeCount((int) $user->id);
                if ($activeCount >= $limit) {
                    return $this->rejectKeywordLimit($request, $limit, 'free_active_limit_reached');
                }
            }
        }

        if ($existing) {
            $existing->update([
                'lang' => $data['lang'] ?? 'und',
            ]);
            $item = $existing;
        } else {
            $item = UserKeyword::create([
                'user_id' => $user->id,
                'emoji_slug' => $data['emoji_slug'],
                'keyword' => $data['keyword'],
                'lang' => $data['lang'] ?? 'und',
                'is_active' => true,
            ]);
        }

        if ($request->expectsJson()) {
            return response()->json(['ok' => true, 'item' => $item]);
        }

        return back()->with('status', 'Keyword saved.');
    }

    public function updateKeyword(Request $request, UserKeyword $keyword): RedirectResponse|JsonResponse
    {
        $user = $request->user();
        if (! $user) {
            abort(403);
        }

        if ($keyword->user_id !== $user->id) {
            abort(403);
        }

        $data = $request->validate([
            'emoji_slug' => 'required|string|max:120',
            'keyword' => 'required|string|max:200',
            'lang' => 'nullable|string|max:10',
        ]);

        $duplicate = UserKeyword::where('user_id', $user->id)
            ->where('emoji_slug', $data['emoji_slug'])
            ->where('keyword', $data['keyword'])
            ->first();

        if ($duplicate && $duplicate->id !== $keyword->id) {
            $keyword->delete();
            $keyword = $duplicate;
        } else {
            $keyword->update([
                'emoji_slug' => $data['emoji_slug'],
                'keyword' => $data['keyword'],
                'lang' => $data['lang'] ?? 'und',
            ]);
        }

        if ($request->expectsJson()) {
            return response()->json(['ok' => true, 'item' => $keyword]);
        }

        return back()->with('status', 'Keyword updated.');
    }

    public function deleteKeyword(Request $request, UserKeyword $keyword): RedirectResponse|JsonResponse
    {
        $user = $request->user();
        if (! $user) {
            abort(403);
        }

        if ($keyword->user_id !== $user->id) {
            abort(403);
        }

        $keyword->delete();

        if ($request->expectsJson()) {
            return response()->json(['ok' => true]);
        }

        return back()->with('status', 'Keyword removed.');
    }

    public function importKeywords(Request $request): RedirectResponse
    {
        $user = $request->user();
        if (! $user) {
            abort(403);
        }

        $payload = $request->input('payload');
        if ($request->hasFile('file')) {
            $payload = $request->file('file')->get();
        }

        $items = json_decode((string) $payload, true);
        if (! is_array($items)) {
            return back()->withErrors(['payload' => 'Invalid JSON payload.']);
        }

        $imported = 0;
        $skipped = 0;
        $limit = $this->keywordLimitFor($user);
        $activeCount = $this->keywordQuota->activeCount((int) $user->id);
        foreach ($items as $row) {
            if (! is_array($row)) {
                continue;
            }
            $emojiSlug = trim((string) ($row['emoji_slug'] ?? ''));
            $keyword = trim((string) ($row['keyword'] ?? ''));
            $lang = trim((string) ($row['lang'] ?? 'und'));
            if ($emojiSlug === '' || $keyword === '') {
                continue;
            }
            $existing = UserKeyword::where('user_id', $user->id)
                ->where('emoji_slug', $emojiSlug)
                ->where('keyword', $keyword)
                ->first();
            $targetActive = filter_var($row['is_active'] ?? true, FILTER_VALIDATE_BOOL);

            if (! $existing && $targetActive && $limit !== null && $activeCount >= $limit) {
                $skipped += 1;

                continue;
            }

            if ($existing) {
                $existing->update([
                    'lang' => $lang !== '' ? $lang : 'und',
                ]);
            } else {
                UserKeyword::create([
                    'user_id' => $user->id,
                    'emoji_slug' => $emojiSlug,
                    'keyword' => $keyword,
                    'lang' => $lang !== '' ? $lang : 'und',
                    'is_active' => $targetActive,
                ]);
                if ($targetActive) {
                    $activeCount += 1;
                }
            }
            $imported += 1;
        }

        $message = "Imported {$imported} keywords.";
        if ($skipped > 0) {
            $message .= " {$skipped} skipped (active free limit reached).";
        }

        return back()->with('status', $message);
    }

    public function exportKeywords(Request $request): BinaryFileResponse
    {
        $user = $request->user();
        if (! $user) {
            abort(403);
        }

        $items = UserKeyword::where('user_id', $user->id)
            ->orderByDesc('id')
            ->get(['emoji_slug', 'keyword', 'lang', 'is_active'])
            ->values()
            ->toJson(JSON_PRETTY_PRINT);

        $filename = 'dewemoji-keywords-'.$user->id.'-'.now()->format('Ymd-His').'.json';
        $path = storage_path('app/'.$filename);
        file_put_contents($path, $items);

        return response()->download($path, $filename)->deleteFileAfterSend(true);
    }

    public function apiKeys(Request $request): View
    {
        $user = $request->user();
        $keys = UserApiKey::where('user_id', $user?->id)
            ->orderByDesc('id')
            ->get();
        $canCreate = $user && (string) $user->tier === 'personal';

        return view('dashboard.user.api-keys', [
            'user' => $user,
            'keys' => $keys,
            'canCreate' => $canCreate,
            'newKey' => session('new_api_key'),
        ]);
    }

    public function createApiKey(Request $request): RedirectResponse
    {
        $user = $request->user();
        if (! $user) {
            abort(403);
        }
        if ((string) $user->tier !== 'personal') {
            return back()->withErrors(['api_key' => 'API keys are available on the Personal plan.']);
        }

        $data = $request->validate([
            'name' => 'nullable|string|max:100',
        ]);

        $issued = $this->keys->issueKey($user, $data['name'] ?? null);

        return back()->with('new_api_key', $issued['plain']);
    }

    public function revokeApiKey(Request $request, UserApiKey $key): RedirectResponse
    {
        $user = $request->user();
        if (! $user || $key->user_id !== $user->id) {
            abort(403);
        }

        $key->update(['revoked_at' => Carbon::now()]);

        return back()->with('status', 'API key revoked.');
    }

    public function billing(Request $request): View
    {
        $user = $request->user();
        $subscription = Subscription::where('user_id', $user?->id)
            ->orderByDesc('started_at')
            ->first();

        $orders = Order::where('user_id', $user?->id)
            ->orderByDesc('id')
            ->limit(5)
            ->get();

        $payments = Payment::where('user_id', $user?->id)
            ->orderByDesc('id')
            ->limit(5)
            ->get();

        return view('dashboard.user.billing', [
            'subscription' => $subscription,
            'user' => $user,
            'orders' => $orders,
            'payments' => $payments,
        ]);
    }

    public function preferences(Request $request): View
    {
        return view('dashboard.user.preferences');
    }

    public function toggleKeywordActive(Request $request, UserKeyword $keyword): RedirectResponse|JsonResponse
    {
        $user = $request->user();
        if (! $user || $keyword->user_id !== $user->id) {
            abort(403);
        }

        $data = $request->validate([
            'is_active' => 'required|boolean',
        ]);

        $target = (bool) $data['is_active'];
        if ($target && ($limit = $this->keywordLimitFor($user))) {
            $activeCount = $this->keywordQuota->activeCount((int) $user->id);
            if (! $keyword->is_active && $activeCount >= $limit) {
                return $this->rejectKeywordLimit($request, $limit, 'free_active_limit_reached');
            }
        }

        $keyword->update(['is_active' => $target]);

        if ($request->expectsJson()) {
            return response()->json(['ok' => true, 'item' => $keyword]);
        }

        return back()->with('status', $target ? 'Keyword activated.' : 'Keyword deactivated.');
    }

    private function rejectKeywordLimit(Request $request, int $limit, string $error = 'free_limit_reached'): RedirectResponse|JsonResponse
    {
        if ($request->expectsJson()) {
            return response()->json(['ok' => false, 'error' => $error, 'limit' => $limit], 403);
        }

        return back()->withErrors(['tier' => "Free plan active limit reached ({$limit} keywords)."]);
    }

    private function keywordLimitFor(?User $user): ?int
    {
        if (! $user) {
            return null;
        }
        if ((string) $user->tier === 'personal') {
            return null;
        }

        return (int) config('dewemoji.pagination.free_max_limit', 20);
    }
}
