<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\PageView;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

/**
 * Lightweight page-view tracking endpoint.
 *
 * Two calls per page visit:
 *  1. POST /api/analytics/track        — record the page view on arrival
 *  2. POST /api/analytics/track-duration — update duration on page leave
 *     (sent via navigator.sendBeacon so it works even during tab close)
 *
 * Rate-limited to 120 req/min per IP (throttle:analytics middleware group).
 * No auth required — tracks both anonymous visitors and authenticated users.
 *
 * Security notes:
 *  - All string inputs are capped and sanitised before storage.
 *  - IP geolocation is resolved via a 24-hour cached lookup (ip-api.com free tier).
 *  - We never store full URL query strings — only the path component.
 *  - UTM params are accepted from the body (already parsed by the JS tracker).
 */
class PageViewController extends Controller
{
    // ── Record a new page view ─────────────────────────────────────────────────
    public function track(Request $request): JsonResponse
    {
        $data = $request->validate([
            'visitor_id'   => 'required|string|max:36',
            'session_id'   => 'required|string|max:36',
            'page'         => 'required|string|max:500',
            'referrer'     => 'nullable|string|max:500',
            'utm_source'   => 'nullable|string|max:100',
            'utm_medium'   => 'nullable|string|max:100',
            'utm_campaign' => 'nullable|string|max:100',
        ]);

        // Strip query string — store only the path
        $data['page'] = parse_url($data['page'], PHP_URL_PATH) ?? $data['page'];
        $data['page'] = mb_substr($data['page'], 0, 500);

        // Sanitise referrer similarly
        if (!empty($data['referrer'])) {
            $parsed = parse_url($data['referrer']);
            $data['referrer'] = isset($parsed['host'])
                ? ($parsed['scheme'] ?? 'https') . '://' . $parsed['host'] . ($parsed['path'] ?? '')
                : null;
        }

        // Network
        $ip        = $request->ip();
        $userAgent = mb_substr($request->userAgent() ?? '', 0, 300);

        // Authenticated context
        $user = $request->user('sanctum'); // optional auth
        $isAuth  = $user !== null;
        $orgId   = $user?->organization_id;

        // IP geo (cached 24 h per IP, never blocks the request)
        $geo = $this->resolveGeo($ip);

        PageView::create([
            'visitor_id'    => $data['visitor_id'],
            'session_id'    => $data['session_id'],
            'page'          => $data['page'],
            'referrer'      => $data['referrer'] ?? null,
            'ip_address'    => $ip,
            'country_code'  => $geo['country_code'] ?? null,
            'country_name'  => $geo['country_name'] ?? null,
            'city'          => $geo['city'] ?? null,
            'user_agent'    => $userAgent,
            'is_authenticated' => $isAuth,
            'organization_id'  => $orgId,
            'utm_source'    => $data['utm_source'] ?? null,
            'utm_medium'    => $data['utm_medium'] ?? null,
            'utm_campaign'  => $data['utm_campaign'] ?? null,
        ]);

        return response()->json(['ok' => true], 201);
    }

    // ── Update page duration on leave ──────────────────────────────────────────
    public function trackDuration(Request $request): JsonResponse
    {
        $data = $request->validate([
            'session_id'       => 'required|string|max:36',
            'page'             => 'required|string|max:500',
            'duration_seconds' => 'required|integer|min:1|max:86400',
        ]);

        $page = parse_url($data['page'], PHP_URL_PATH) ?? $data['page'];

        // Update the most recent matching page view for this session
        PageView::where('session_id', $data['session_id'])
            ->where('page', $page)
            ->whereNull('duration_seconds')
            ->latest()
            ->limit(1)
            ->update(['duration_seconds' => $data['duration_seconds']]);

        return response()->json(['ok' => true]);
    }

    // ── IP → Country / City (free ip-api.com, cached 24 h) ────────────────────
    private function resolveGeo(string $ip): array
    {
        // Skip for private / loopback addresses
        if ($this->isPrivateIp($ip)) {
            return [];
        }

        $cacheKey = 'geo_' . md5($ip);

        return Cache::remember($cacheKey, now()->addHours(24), function () use ($ip) {
            try {
                $response = Http::timeout(2)
                    ->get("http://ip-api.com/json/{$ip}", [
                        'fields' => 'status,country,countryCode,city',
                    ]);

                if ($response->ok()) {
                    $body = $response->json();
                    if (($body['status'] ?? '') === 'success') {
                        return [
                            'country_code' => $body['countryCode'] ?? null,
                            'country_name' => $body['country'] ?? null,
                            'city'         => $body['city'] ?? null,
                        ];
                    }
                }
            } catch (\Throwable $e) {
                Log::debug('[GeoLookup] failed for ' . $ip . ': ' . $e->getMessage());
            }
            return [];
        });
    }

    private function isPrivateIp(string $ip): bool
    {
        return filter_var($ip, FILTER_VALIDATE_IP,
            FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false;
    }
}
