<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use App\Models\Payment;
use App\Models\PricingPlan;
use App\Models\Subscription;
use App\Models\UserKeyword;
use App\Services\System\SettingsService;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

class SiteController extends Controller
{
    /** @var array<string,string> */
    private const CATEGORY_TO_SLUG = [
        'Smileys & Emotion' => 'smileys',
        'People & Body' => 'people',
        'Animals & Nature' => 'animals',
        'Food & Drink' => 'food',
        'Travel & Places' => 'travel',
        'Activities' => 'activities',
        'Objects' => 'objects',
        'Symbols' => 'symbols',
        'Flags' => 'flags',
    ];

    private function billingMode(): string
    {
        $settings = app(SettingsService::class);
        $preferred =
            (string) ($settings->get(
                'billing_mode',
                config('dewemoji.billing.mode', 'sandbox'),
            ) ?:
            'sandbox');
        if ($this->paypalConfiguredMode($preferred)) {
            return $preferred;
        }

        $fallback = $preferred === 'live' ? 'sandbox' : 'live';
        if ($this->paypalConfiguredMode($fallback)) {
            return $fallback;
        }

        return $preferred;
    }

    public function home(Request $request): View
    {
        return view('site.home', [
            'initialQuery' => trim((string) $request->query('q', '')),
            'initialCategory' => trim((string) $request->query('category', '')),
            'initialSubcategory' => trim(
                (string) $request->query('subcategory', ''),
            ),
            'canonicalPath' => '/',
            'userTier' => $request->user()?->tier,
        ]);
    }

    public function browse(Request $request): RedirectResponse|View
    {
        $cat = strtolower(trim((string) $request->query('cat', 'all')));
        if (
            $cat !== '' &&
            $cat !== 'all' &&
            array_key_exists($cat, $this->categorySlugMap())
        ) {
            return redirect('/'.$cat, 301);
        }

        return view('site.home', [
            'initialQuery' => trim((string) $request->query('q', '')),
            'initialCategory' => trim((string) $request->query('category', '')),
            'initialSubcategory' => trim(
                (string) $request->query('subcategory', ''),
            ),
            'canonicalPath' => '/browse',
            'userTier' => $request->user()?->tier,
        ]);
    }

    public function category(string $categorySlug): View
    {
        if ($categorySlug === 'all') {
            return view('site.home', [
                'initialQuery' => '',
                'initialCategory' => '',
                'initialSubcategory' => '',
                'canonicalPath' => '/',
                'userTier' => request()->user()?->tier,
            ]);
        }

        $categoryLabel = $this->categorySlugMap()[$categorySlug] ?? '';
        abort_if($categoryLabel === '', 404);

        return view('site.home', [
            'initialQuery' => '',
            'initialCategory' => $categoryLabel,
            'initialSubcategory' => '',
            'canonicalPath' => '/'.$categorySlug,
            'userTier' => request()->user()?->tier,
        ]);
    }

    public function categorySubcategory(
        string $categorySlug,
        string $subcategorySlug,
    ): View {
        if ($categorySlug === 'all') {
            abort(404);
        }

        $categoryLabel = $this->categorySlugMap()[$categorySlug] ?? '';
        abort_if($categoryLabel === '', 404);

        return view('site.home', [
            'initialQuery' => '',
            'initialCategory' => $categoryLabel,
            'initialSubcategory' => $subcategorySlug,
            'canonicalPath' => '/'.$categorySlug.'/'.$subcategorySlug,
            'userTier' => request()->user()?->tier,
        ]);
    }

    public function apiDocs(): View
    {
        return view('site.api-docs');
    }

    public function pricing(): View
    {
        $user = request()->user();
        $currencyPref = strtoupper((string) session('pricing_currency', ''));
        if (! in_array($currencyPref, ['IDR', 'USD'], true)) {
            $currencyPref = $this->detectPricingCurrency(request());
            session(['pricing_currency' => $currencyPref]);
        }

        $rate = (int) config('dewemoji.pricing.usd_rate', 15000);
        $plans = PricingPlan::where('status', 'active')->get()->keyBy('code');
        $defaults = config('dewemoji.pricing.defaults', []);
        $fallback = collect($defaults)->keyBy('code');

        $getPlanAmount = function (string $code) use ($plans, $fallback): int {
            $plan = $plans->get($code) ?? $fallback->get($code);

            return (int) ($plan['amount'] ?? ($plan->amount ?? 0));
        };

        $pricing = [
            'personal_monthly' => [
                'idr' => $getPlanAmount('personal_monthly'),
            ],
            'personal_annual' => [
                'idr' => $getPlanAmount('personal_annual'),
            ],
            'personal_lifetime' => [
                'idr' => $getPlanAmount('personal_lifetime'),
            ],
        ];

        foreach ($pricing as $key => $row) {
            $pricing[$key]['usd'] =
                $rate > 0 ? round($row['idr'] / $rate, 2) : 0;
        }

        $hasActiveLifetime = false;
        $hasPendingPayment = false;
        $pendingCooldownRemaining = 0;
        if ($user) {
            $hasActiveLifetime = Subscription::query()
                ->where('user_id', $user->id)
                ->where('plan', 'personal_lifetime')
                ->where('status', 'active')
                ->where(function ($query) {
                    $query
                        ->whereNull('expires_at')
                        ->orWhere('expires_at', '>', now());
                })
                ->exists();
            $hasPendingPayment = Payment::query()
                ->where('user_id', $user->id)
                ->where('status', 'pending')
                ->exists();
            $cooldown = (int) config(
                'dewemoji.billing.pending_cooldown_seconds',
                120,
            );
            if ($cooldown > 0) {
                $latestPending = Payment::query()
                    ->where('user_id', $user->id)
                    ->where('status', 'pending')
                    ->orderByDesc('id')
                    ->first();
                if ($latestPending && $latestPending->created_at) {
                    $age = max(
                        0,
                        now()->getTimestamp() -
                            $latestPending->created_at->getTimestamp(),
                    );
                    $pendingCooldownRemaining = max(0, $cooldown - $age);
                }
            }
        }

        return view('site.pricing', [
            'currencyPref' => $currencyPref,
            'usdRate' => $rate,
            'pricing' => $pricing,
            'payments' => [
                'qris_url' => (string) config('dewemoji.payments.qris_url', ''),
                'paypal_url' => (string) config(
                    'dewemoji.payments.paypal_url',
                    '',
                ),
            ],
            'pakasirEnabled' => (bool) config(
                'dewemoji.billing.providers.pakasir.enabled',
                false,
            ) &&
                (string) config(
                    'dewemoji.billing.providers.pakasir.api_base',
                    '',
                ) !== '' &&
                (string) config(
                    'dewemoji.billing.providers.pakasir.api_key',
                    '',
                ) !== '' &&
                (string) config(
                    'dewemoji.billing.providers.pakasir.project',
                    '',
                ) !== '',
            'paypalEnabled' => $this->paypalEnabled($this->billingMode()),
            'paypalPlans' => $this->paypalPlanAvailability(
                $this->billingMode(),
            ),
            'hasActiveLifetime' => $hasActiveLifetime,
            'hasPendingPayment' => $hasPendingPayment,
            'pendingCooldownRemaining' => $pendingCooldownRemaining,
        ]);
    }

    private function paypalEnabled(string $mode): bool
    {
        if ($this->paypalConfiguredMode($mode)) {
            return true;
        }

        $fallback = $mode === 'live' ? 'sandbox' : 'live';

        return $this->paypalConfiguredMode($fallback);
    }

    private function paypalConfiguredMode(string $mode): bool
    {
        $enabled = (bool) config(
            'dewemoji.billing.providers.paypal.enabled',
            false,
        );
        $clientId = (string) config(
            "dewemoji.billing.providers.paypal.{$mode}.client_id",
            '',
        );
        $clientSecret = (string) config(
            "dewemoji.billing.providers.paypal.{$mode}.client_secret",
            '',
        );
        $apiBase = (string) config(
            "dewemoji.billing.providers.paypal.{$mode}.api_base",
            '',
        );

        return $enabled &&
            $clientId !== '' &&
            $clientSecret !== '' &&
            $apiBase !== '';
    }

    private function paypalPlanAvailability(string $mode): array
    {
        $plans = PricingPlan::whereIn('code', [
            'personal_monthly',
            'personal_annual',
        ])
            ->get()
            ->keyBy('code');

        $fromDb = function (string $code) use ($plans, $mode): bool {
            $plan = $plans->get($code);
            if (! $plan) {
                return false;
            }
            $meta = $plan->meta ?? [];

            return (string) ($meta['paypal'][$mode]['plan']['id'] ?? '') !== '';
        };

        $fromEnv = function (string $code) use ($mode): bool {
            return (string) config(
                "dewemoji.billing.providers.paypal.plan_ids.{$mode}.{$code}",
                '',
            ) !== '';
        };

        return [
            'personal_monthly' => $fromDb('personal_monthly') || $fromEnv('personal_monthly'),
            'personal_annual' => $fromDb('personal_annual') || $fromEnv('personal_annual'),
        ];
    }

    public function setPricingCurrency(Request $request): RedirectResponse
    {
        $data = $request->validate([
            'currency' => 'required|string|in:IDR,USD',
        ]);

        session(['pricing_currency' => $data['currency']]);

        return back();
    }

    public function support(): View
    {
        return view('site.support');
    }

    public function download(): View
    {
        $downloadBaseUrl = rtrim(
            (string) config('dewemoji.apk_release.public_base_url', ''),
            '/',
        );
        $androidEnabled = false;

        return view('site.download', [
            'androidEnabled' => $androidEnabled,
            'androidVersionJsonUrl' => $androidEnabled
                ? $downloadBaseUrl.'/version.json'
                : '',
            'androidLatestApkUrl' => $androidEnabled
                ? $downloadBaseUrl.'/dewemoji-latest.apk'
                : '',
        ]);
    }

    public function downloadVersionJson(
        Request $request,
    ): RedirectResponse|JsonResponse {
        $target = $this->apkReleaseTargetUrl('version_json');
        if ($target === '') {
            return response()->json(
                ['ok' => false, 'error' => 'apk_release_not_configured'],
                404,
            );
        }

        return redirect()->away($target, 302, [
            'Cache-Control' => 'no-store, no-cache, must-revalidate',
            'Pragma' => 'no-cache',
        ]);
    }

    public function downloadLatestApk(
        Request $request,
    ): RedirectResponse|JsonResponse {
        $target = $this->apkReleaseTargetUrl('latest_apk');
        if ($target === '') {
            return response()->json(
                ['ok' => false, 'error' => 'apk_release_not_configured'],
                404,
            );
        }

        return redirect()->away($target, 302, [
            'Cache-Control' => 'no-store, no-cache, must-revalidate',
            'Pragma' => 'no-cache',
        ]);
    }

    public function assetLinks(): JsonResponse
    {
        $appId = trim((string) config('dewemoji.apk_release.app_id', ''));
        $rawFingerprints = (array) config(
            'dewemoji.apk_release.assetlinks.fingerprints',
            [],
        );
        $fingerprints = [];
        foreach ($rawFingerprints as $fingerprint) {
            $normalized = $this->normalizeApkCertFingerprint(
                (string) $fingerprint,
            );
            if ($normalized !== '') {
                $fingerprints[] = $normalized;
            }
        }
        $fingerprints = array_values(array_unique($fingerprints));

        if ($appId === '' || $fingerprints === []) {
            return response()->json([], 200, [
                'Cache-Control' => 'no-store, no-cache, must-revalidate',
                'Pragma' => 'no-cache',
            ]);
        }

        return response()->json(
            [
                [
                    'relation' => [
                        'delegate_permission/common.handle_all_urls',
                    ],
                    'target' => [
                        'namespace' => 'android_app',
                        'package_name' => $appId,
                        'sha256_cert_fingerprints' => $fingerprints,
                    ],
                ],
            ],
            200,
            [
                'Cache-Control' => 'no-store, no-cache, must-revalidate',
                'Pragma' => 'no-cache',
            ],
        );
    }

    public function privacy(): View
    {
        return view('site.privacy');
    }

    public function terms(): View
    {
        return view('site.terms');
    }

    private function detectPricingCurrency(Request $request): string
    {
        $country = strtoupper(
            (string) ($request->header('CF-IPCountry') ??
                ($request->header('X-Country-Code') ??
                    ($request->header('X-Geo-Country') ??
                        ($request->header('X-Appengine-Country') ??
                            ($request->header('CloudFront-Viewer-Country') ??
                                ''))))),
        );

        return $country === 'ID' ? 'IDR' : 'USD';
    }

    public function emojiDetail(string $slug): View|Response
    {
        $dataPath = $this->datasetPath();
        if (! is_file($dataPath)) {
            abort(500, 'Emoji dataset file not found.');
        }

        $raw = file_get_contents($dataPath);
        if ($raw === false) {
            abort(500, 'Emoji dataset file could not be read.');
        }

        $decoded = json_decode($raw, true);
        if (! is_array($decoded)) {
            abort(500, 'Emoji dataset JSON is invalid.');
        }

        $items = $decoded['emojis'] ?? [];
        $match = null;
        $byEmoji = [];
        foreach ($items as $item) {
            $char = (string) ($item['emoji'] ?? '');
            if ($char !== '' && ! isset($byEmoji[$char])) {
                $byEmoji[$char] = $item;
            }
            if (($item['slug'] ?? '') === $slug) {
                $match = $item;
            }
        }

        if (! $match) {
            abort(404);
        }

        $relatedDetails = [];
        foreach (array_slice($match['related'] ?? [], 0, 8) as $relatedEmoji) {
            $relatedEmoji = (string) $relatedEmoji;
            $ref = $byEmoji[$relatedEmoji] ?? null;
            $relatedDetails[] = [
                'emoji' => $relatedEmoji,
                'slug' => (string) ($ref['slug'] ?? ''),
                'name' => (string) ($ref['name'] ?? $relatedEmoji),
            ];
        }

        $user = request()->user();
        $canManageKeywords = (bool) $user;
        $isPersonal = $user && (string) $user->tier === 'personal';
        $freeLimit = (int) config('dewemoji.pagination.free_max_limit', 20);
        $keywordLimit = $isPersonal ? null : $freeLimit;
        $userKeywords = [];
        $activeKeywordCount = 0;
        if ($canManageKeywords) {
            $activeKeywordCount = UserKeyword::where('user_id', $user->id)
                ->where('is_active', true)
                ->count();
            $userKeywords = UserKeyword::where('user_id', $user->id)
                ->where('emoji_slug', $slug)
                ->orderByDesc('id')
                ->get();
        }
        $limitReached =
            $keywordLimit !== null && $activeKeywordCount >= $keywordLimit;

        return view('site.emoji-detail', [
            'emoji' => $match,
            'relatedDetails' => $relatedDetails,
            'canonicalPath' => '/emoji/'.$slug,
            'userKeywords' => $userKeywords,
            'canManageKeywords' => $canManageKeywords,
            'keywordLimit' => $keywordLimit,
            'limitReached' => $limitReached,
            'activeKeywordCount' => $activeKeywordCount,
            'userTier' => $user?->tier,
        ]);
    }

    public function robotsTxt(): Response
    {
        $base = rtrim(
            config('app.url', request()->getSchemeAndHttpHost()),
            '/',
        );
        $body =
            "User-agent: *\nAllow: /\n\nSitemap: ".$base."/sitemap.xml\n";

        return response($body, 200)->header(
            'Content-Type',
            'text/plain; charset=UTF-8',
        );
    }

    public function sitemapXml(): Response
    {
        $data = $this->loadDataset();
        $items = is_array($data['emojis'] ?? null) ? $data['emojis'] : [];
        $base = rtrim(
            config('app.url', request()->getSchemeAndHttpHost()),
            '/',
        );

        $lastUpdatedTs = isset($data['last_updated_ts'])
            ? (int) $data['last_updated_ts']
            : time();
        $lastUpdated = gmdate("Y-m-d\TH:i:s\Z", $lastUpdatedTs);

        $urls = [
            [
                'loc' => $base.'/',
                'priority' => '0.8',
                'changefreq' => 'daily',
            ],
            [
                'loc' => $base.'/api-docs',
                'priority' => '0.5',
                'changefreq' => 'weekly',
            ],
            [
                'loc' => $base.'/pricing',
                'priority' => '0.7',
                'changefreq' => 'weekly',
            ],
            [
                'loc' => $base.'/privacy',
                'priority' => '0.3',
                'changefreq' => 'monthly',
            ],
            [
                'loc' => $base.'/terms',
                'priority' => '0.3',
                'changefreq' => 'monthly',
            ],
            [
                'loc' => $base.'/support',
                'priority' => '0.4',
                'changefreq' => 'weekly',
            ],
        ];

        foreach ($items as $item) {
            $slug = trim((string) ($item['slug'] ?? ''));
            if ($slug === '' || $this->shouldHideForSitemap($item)) {
                continue;
            }
            $urls[] = [
                'loc' => $base.'/emoji/'.$slug,
                'priority' => '0.6',
                'changefreq' => 'weekly',
            ];
        }

        $xml = '<?xml version="1.0" encoding="UTF-8"?>'."\n";
        $xml .=
            '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'.
            "\n";
        foreach ($urls as $url) {
            $xml .= "  <url>\n";
            $xml .=
                '    <loc>'.
                htmlspecialchars((string) $url['loc'], ENT_XML1).
                "</loc>\n";
            $xml .= '    <lastmod>'.$lastUpdated."</lastmod>\n";
            $xml .= '    <changefreq>'.$url['changefreq']."</changefreq>\n";
            $xml .= '    <priority>'.$url['priority']."</priority>\n";
            $xml .= "  </url>\n";
        }
        $xml .= '</urlset>'."\n";

        return response($xml, 200)->header(
            'Content-Type',
            'application/xml; charset=UTF-8',
        );
    }

    /**
     * @return array<string,string>
     */
    private function categorySlugMap(): array
    {
        $out = [];
        foreach (self::CATEGORY_TO_SLUG as $label => $slug) {
            $out[$slug] = $label;
        }

        return $out;
    }

    /**
     * @return array<string,mixed>
     */
    private function loadDataset(): array
    {
        $dataPath = $this->datasetPath();
        if (! is_file($dataPath)) {
            return ['emojis' => []];
        }

        $raw = file_get_contents($dataPath);
        if ($raw === false) {
            return ['emojis' => []];
        }

        $decoded = json_decode($raw, true);
        if (! is_array($decoded)) {
            return ['emojis' => []];
        }

        return $decoded;
    }

    private function datasetPath(): string
    {
        $settings = app(SettingsService::class);
        $activePath = (string) $settings->get('emoji_dataset_active_path', '');
        if ($activePath !== '' && is_file($activePath)) {
            return $activePath;
        }

        return (string) config('dewemoji.data_path');
    }

    private function apkReleaseTargetUrl(string $key): string
    {
        if (! (bool) config('dewemoji.apk_release.enabled', false)) {
            return '';
        }

        $base = trim(
            (string) config('dewemoji.apk_release.r2_public_base_url', ''),
        );
        $objectKey = trim(
            (string) config("dewemoji.apk_release.r2_keys.{$key}", ''),
        );
        if ($base === '' || $objectKey === '') {
            return '';
        }

        return rtrim($base, '/').'/'.ltrim($objectKey, '/');
    }

    private function normalizeApkCertFingerprint(string $value): string
    {
        $clean = strtoupper(trim($value));
        if ($clean === '') {
            return '';
        }

        if (preg_match('/^[0-9A-F]{64}$/', $clean) === 1) {
            return implode(':', str_split($clean, 2));
        }

        if (preg_match('/^[0-9A-F]{2}(?::[0-9A-F]{2}){31}$/', $clean) === 1) {
            return $clean;
        }

        return '';
    }

    /**
     * @param  array<string,mixed>  $emoji
     */
    private function shouldHideForSitemap(array $emoji): bool
    {
        $name = strtolower(trim((string) ($emoji['name'] ?? '')));
        $category = strtolower(trim((string) ($emoji['category'] ?? '')));
        $subcategory = strtolower(trim((string) ($emoji['subcategory'] ?? '')));

        if ($subcategory === 'family' || str_starts_with($name, 'family:')) {
            return true;
        }
        if (preg_match("~\bwoman: beard\b~i", $name)) {
            return true;
        }
        if (preg_match("~\bmen with bunny ears\b~i", $name)) {
            return true;
        }
        if (preg_match("~\bpregnant man\b~i", $name)) {
            return true;
        }
        if ($category === 'people & body') {
            if (preg_match("~\bmen holding hands\b~i", $name)) {
                return true;
            }
            if (preg_match("~\bwomen holding hands\b~i", $name)) {
                return true;
            }
            if (preg_match("~kiss:.*\bman,\s*man\b~i", $name)) {
                return true;
            }
            if (preg_match("~kiss:.*\bwoman,\s*woman\b~i", $name)) {
                return true;
            }
            if (preg_match("~couple.*\bman,\s*man\b~i", $name)) {
                return true;
            }
            if (preg_match("~couple.*\bwoman,\s*woman\b~i", $name)) {
                return true;
            }
        }

        return false;
    }
}
