<?php

namespace App\Services;

use App\Models\AbTest;
use App\Models\AbTestAssignment;
use App\Models\AbTestEvent;
use Illuminate\Support\Facades\Cache;

class AbTestService
{
    protected const CACHE_PREFIX = 'ab_test:';
    protected const CACHE_TTL = 86400; // 24 hours

    /**
     * Get variant for a user in an A/B test
     */
    public function getVariant(string $testKey, ?string $userIdentifier = null): ?string
    {
        $userIdentifier = $userIdentifier ?? $this->getUserIdentifier();

        return Cache::remember(
            self::CACHE_PREFIX . $testKey . ':' . $userIdentifier,
            self::CACHE_TTL,
            function () use ($testKey, $userIdentifier) {
                $test = AbTest::where('key', $testKey)
                    ->where('is_active', true)
                    ->first();

                if (!$test || !$test->isActive()) {
                    return null;
                }

                // Check if user already has an assignment
                $assignment = AbTestAssignment::where('ab_test_id', $test->id)
                    ->where('user_identifier', $userIdentifier)
                    ->first();

                if ($assignment) {
                    return $assignment->variant;
                }

                // Assign variant based on distribution
                $variant = $this->assignVariant($test, $userIdentifier);
                
                return $variant;
            }
        );
    }

    /**
     * Track an event for an A/B test
     */
    public function trackEvent(string $testKey, string $eventType, array $eventData = [], ?string $userIdentifier = null): void
    {
        $userIdentifier = $userIdentifier ?? $this->getUserIdentifier();
        
        $test = AbTest::where('key', $testKey)->first();
        
        if (!$test) {
            return;
        }

        $assignment = AbTestAssignment::where('ab_test_id', $test->id)
            ->where('user_identifier', $userIdentifier)
            ->first();

        if (!$assignment) {
            return;
        }

        AbTestEvent::create([
            'ab_test_id' => $test->id,
            'user_identifier' => $userIdentifier,
            'variant' => $assignment->variant,
            'event_type' => $eventType,
            'event_data' => $eventData,
            'occurred_at' => now(),
        ]);
    }

    /**
     * Get test statistics
     */
    public function getStatistics(string $testKey): array
    {
        $test = AbTest::where('key', $testKey)->first();
        
        if (!$test) {
            return [];
        }

        $variants = $test->variants ?? [];
        $stats = [];

        foreach (array_keys($variants) as $variant) {
            $assignments = AbTestAssignment::where('ab_test_id', $test->id)
                ->where('variant', $variant)
                ->count();

            $views = AbTestEvent::where('ab_test_id', $test->id)
                ->where('variant', $variant)
                ->where('event_type', 'view')
                ->count();

            $conversions = AbTestEvent::where('ab_test_id', $test->id)
                ->where('variant', $variant)
                ->where('event_type', 'conversion')
                ->count();

            $conversionRate = $views > 0 ? ($conversions / $views) * 100 : 0;

            $stats[$variant] = [
                'assignments' => $assignments,
                'views' => $views,
                'conversions' => $conversions,
                'conversion_rate' => round($conversionRate, 2),
            ];
        }

        return $stats;
    }

    /**
     * Assign a variant to a user
     */
    protected function assignVariant(AbTest $test, string $userIdentifier): string
    {
        $variants = $test->variants ?? ['A' => 50, 'B' => 50];
        
        // Create weighted random selection
        $total = array_sum($variants);
        $random = mt_rand(1, $total);
        $current = 0;

        foreach ($variants as $variant => $weight) {
            $current += $weight;
            if ($random <= $current) {
                // Save assignment
                AbTestAssignment::create([
                    'ab_test_id' => $test->id,
                    'user_identifier' => $userIdentifier,
                    'variant' => $variant,
                    'assigned_at' => now(),
                ]);

                return $variant;
            }
        }

        // Fallback to first variant
        return array_key_first($variants);
    }

    /**
     * Get user identifier (user ID or session ID)
     */
    protected function getUserIdentifier(): string
    {
        if (auth()->check()) {
            return 'user_' . auth()->id();
        }

        return 'session_' . session()->getId();
    }
}

