<?php

namespace App\Http\Controllers\User;

use App\Http\Controllers\Controller;
use App\Models\Campaign;
use App\Models\Offerwall;
use App\Models\OfferwallClick;
use App\Models\ProviderOffer;
use App\Support\DirectOfferFeed;
use App\Support\GeoLocation;
use App\Support\OfferAudience;
use App\Support\OfferCurator;
use App\Support\ProviderOfferImporter;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;

class EarnController extends Controller
{
    /** Offers rendered per lazy-loaded chunk. */
    private const CHUNK = 24;

    public function index(Request $request)
    {
        if (site_bool('protection_verify_email_earn') && is_null($request->user()->email_verified_at)) {
            return redirect()->route('verification.notice');
        }

        $offerwalls = Offerwall::where('is_active', true)
            ->when(OfferAudience::deviceForRequest($request) === 'desktop', fn ($query) => $query->where('is_mobile', false))
            ->withCount('clicks')
            ->orderByDesc('clicks_count')
            ->orderBy('name')
            ->get();

        $providerOffers = DirectOfferFeed::activeProviderOffers();
        $directOffers = DirectOfferFeed::from($offerwalls, $providerOffers);
        $filters = $this->filters($request);

        $filteredOffers = DirectOfferFeed::filtered($directOffers, $filters);
        $shelfOffers = DirectOfferFeed::filtered($directOffers, [
            'type' => $filters['type'],
            'search' => '',
            'provider' => $filters['provider'],
            'device' => $filters['device'],
            'sort' => 'popular',
        ]);

        // Only the first chunk is server-rendered; the rest stream in on scroll.
        $firstChunk = $filteredOffers->take(self::CHUNK)->values();

        return view('user.earn', [
            'offerwalls' => $offerwalls,
            'topOffers' => DirectOfferFeed::hot($shelfOffers, 10),
            'featuredOffers' => DirectOfferFeed::featured($shelfOffers, 10),
            'offerGrid' => $firstChunk,
            'offerTotal' => $filteredOffers->count(),
            'hasMoreOffers' => $filteredOffers->count() > self::CHUNK,
            'providers' => $offerwalls->where('type', 'offer')->values(),
            'surveys' => $offerwalls->where('type', 'survey')->values(),
            'providerOptions' => $directOffers->pluck('provider')->unique()->sort()->values(),
            'categoryCounts' => DirectOfferFeed::counts($directOffers, $offerwalls),
            'filters' => $filters,
            'selectedType' => $filters['type'],
        ]);
    }

    /**
     * AJAX endpoint: return one rendered chunk of the filtered offer grid so
     * the earn page can load offers lazily as the user scrolls.
     */
    public function offers(Request $request)
    {
        if (site_bool('protection_verify_email_earn') && is_null($request->user()->email_verified_at)) {
            abort(403);
        }

        $page = max(1, (int) $request->query('page', 1));
        $offerwalls = Offerwall::where('is_active', true)
            ->when(OfferAudience::deviceForRequest($request) === 'desktop', fn ($query) => $query->where('is_mobile', false))
            ->get();
        $providerOffers = DirectOfferFeed::activeProviderOffers();
        $directOffers = DirectOfferFeed::from($offerwalls, $providerOffers);
        $filtered = DirectOfferFeed::filtered($directOffers, $this->filters($request));

        $chunk = $filtered->slice(($page - 1) * self::CHUNK, self::CHUNK)->values();
        $hasMore = $filtered->count() > $page * self::CHUNK;

        $html = '';
        foreach ($chunk as $offer) {
            $html .= view('user.partials.direct-offer-card', ['offer' => $offer, 'variant' => 'grid'])->render();
        }

        return response()->json([
            'html' => $html,
            'hasMore' => $hasMore,
            'nextPage' => $page + 1,
        ]);
    }

    public function state()
    {
        return response()->json([
            'version' => $this->feedVersion(),
        ])->header('Cache-Control', 'no-store, private');
    }

    /**
     * Refresh stale provider feeds after the member opens/reloads Earn.
     * A shared lock and per-provider freshness window keep page traffic from
     * becoming provider traffic while still making newly published offers
     * visible without waiting for the server cron.
     */
    public function refresh(ProviderOfferImporter $importer, OfferCurator $curator)
    {
        $before = $this->feedVersion();
        $lock = Cache::lock('provider-offers:user-refresh', 90);

        if (!$lock->get()) {
            return response()->json(['refreshed' => false, 'changed' => false, 'version' => $before]);
        }

        $refreshed = 0;

        try {
            Offerwall::where('is_active', true)->orderBy('id')->get()->each(function (Offerwall $offerwall) use ($importer, &$refreshed) {
                if (!$importer->enabled($offerwall) || !$importer->configured($offerwall) || !$this->feedNeedsRefresh($offerwall)) {
                    return;
                }

                $importer->sync($offerwall);
                $refreshed++;
            });

            if ($refreshed > 0) {
                $curator->curate();
            }
        } finally {
            $lock->release();
        }

        $after = $this->feedVersion();

        return response()->json([
            'refreshed' => $refreshed > 0,
            'changed' => $before !== $after,
            'version' => $after,
        ])->header('Cache-Control', 'no-store, private');
    }

    private function feedNeedsRefresh(Offerwall $offerwall): bool
    {
        $lastChecked = data_get($offerwall->sdk_data, 'offer_feed_last_checked_at');

        if (!$lastChecked) {
            return true;
        }

        try {
            return Carbon::parse($lastChecked)->lt(now()->subMinutes(max(1, (int) config('rewards.user_offer_refresh_minutes', 5))));
        } catch (\Throwable) {
            return true;
        }
    }

    private function feedVersion(): string
    {
        $providerQuery = ProviderOffer::where('is_active', true)
            ->whereHas('offerwall', fn ($offerwall) => $offerwall->where('is_active', true));
        $campaignQuery = Campaign::where('status', 'active');

        return sha1(implode('|', [
            (string) $providerQuery->max('updated_at'),
            (string) $providerQuery->max('last_synced_at'),
            (string) $providerQuery->count(),
            (string) $campaignQuery->max('updated_at'),
            (string) $campaignQuery->count(),
        ]));
    }

    private function filters(Request $request): array
    {
        return [
            'type' => $request->query('type', 'all'),
            'search' => $request->query('q', $request->query('search', '')),
            'provider' => $request->query('provider', ''),
            'device' => $request->query('device', ''),
            'sort' => $request->query('sort', 'popular'),
        ];
    }

    public function open(Request $request, Offerwall $offerwall)
    {
        abort_unless($offerwall->is_active, 404);

        OfferwallClick::create([
            'user_id' => $request->user()->id,
            'offerwall_id' => $offerwall->id,
            'provider_name' => $offerwall->name,
            'launch_mode' => $offerwall->launch_mode ?? 'iframe',
            'ip' => $request->ip(),
            'user_agent' => substr((string) $request->userAgent(), 0, 255),
        ]);

        return response()->json([
            'id' => $offerwall->id,
            'name' => $offerwall->name,
            'url' => $offerwall->launchUrlFor($request->user()),
            'launch_mode' => $offerwall->launch_mode ?? 'iframe',
            'description' => $offerwall->description,
        ]);
    }

    public function openCampaign(Request $request, Campaign $campaign)
    {
        $campaign->load('offerwall');

        abort_unless($campaign->status === 'active', 404);
        abort_if($campaign->starts_at && $campaign->starts_at->isFuture(), 404);
        abort_if($campaign->ends_at && $campaign->ends_at->isPast(), 404);
        abort_unless($campaign->offerwall && $campaign->offerwall->is_active, 404);

        $url = $campaign->launchUrlFor($request->user());

        OfferwallClick::create([
            'user_id' => $request->user()->id,
            'offerwall_id' => $campaign->offerwall_id,
            'campaign_id' => $campaign->id,
            'provider_name' => $campaign->offerwall->name,
            'offer_name' => $campaign->name,
            'offer_url' => $url,
            'launch_mode' => $campaign->direct_launch_mode ?: ($campaign->offerwall->launch_mode ?? 'iframe'),
            'ip' => $request->ip(),
            'user_agent' => substr((string) $request->userAgent(), 0, 255),
        ]);

        return response()->json([
            'id' => $campaign->id,
            'name' => $campaign->name,
            'provider' => $campaign->offerwall->name,
            'url' => $url,
            'launch_mode' => $campaign->direct_launch_mode ?: ($campaign->offerwall->launch_mode ?? 'iframe'),
            'description' => $campaign->description,
            'reward' => (float) $campaign->reward_points,
            'category' => $campaign->category,
            'devices' => [$campaign->device ?: 'all'],
            'steps' => [$campaign->description ?: 'Start the offer and complete the listed requirements.'],
            'rewards' => [[
                'coins' => (int) max(1, round((float) $campaign->reward_points)),
                'label' => $campaign->description ?: 'Complete the offer.',
            ]],
        ]);
    }

    public function openProviderOffer(Request $request, ProviderOffer $providerOffer)
    {
        $providerOffer->load('offerwall');

        abort_unless($providerOffer->is_active, 404);
        abort_unless($providerOffer->offerwall && $providerOffer->offerwall->is_active, 404);
        abort_unless($providerOffer->offerwall->hasLiveApiFeed(), 404);
        abort_unless(OfferAudience::allows(
            $providerOffer,
            GeoLocation::countryForRequest(),
            OfferAudience::deviceForRequest($request)
        ), 404);

        $url = $providerOffer->launchUrlFor($request->user());
        $launchMode = $providerOffer->launch_mode ?: ($providerOffer->offerwall->launch_mode ?? 'iframe');

        abort_if($url === '', 404);

        OfferwallClick::create([
            'user_id' => $request->user()->id,
            'offerwall_id' => $providerOffer->offerwall_id,
            'provider_offer_id' => $providerOffer->id,
            'provider_name' => $providerOffer->offerwall->name,
            'offer_name' => $providerOffer->name,
            'offer_url' => $url,
            'launch_mode' => $launchMode,
            'ip' => $request->ip(),
            'user_agent' => substr((string) $request->userAgent(), 0, 255),
        ]);

        return response()->json([
            'id' => $providerOffer->id,
            'name' => $providerOffer->name,
            'provider' => $providerOffer->offerwall->name,
            'url' => $url,
            'launch_mode' => $launchMode,
            'description' => $providerOffer->description,
            'requirements' => $providerOffer->requirements,
            'reward' => (float) $providerOffer->reward_points,
            'category' => $providerOffer->category,
            'devices' => $providerOffer->normalizedDevices(),
            'steps' => $providerOffer->steps ?: [],
            'rewards' => $providerOffer->rewards ?: [],
        ]);
    }
}
