<?php

namespace App\Http\Controllers;

use App\Enums\LandingPageLayoutMode;
use App\Jobs\GenerateCatalogLandingPagesJob;
use App\Models\LandingPage;
use App\Models\LandingPageGeneration;
use App\Services\Catalog\CatalogProductImportValidator;
use App\Services\CatalogTemplateLocator;
use App\Support\CatalogAffiliateRegistry;
use App\Support\PromptLandingPagePixels;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Validator;
use Illuminate\View\View;

class CatalogLandingPageGenerationController extends Controller
{
    public function index(Request $request): RedirectResponse
    {
        return redirect()->route('landing-pages.index', array_merge(
            ['tab' => 'catalog'],
            $request->query(),
        ));
    }

    /**
     * @return array{
     *     generations: LengthAwarePaginator,
     *     stats: array{all: int, completed: int, active: int, failed: int},
     *     sort: string,
     *     dir: string
     * }
     */
    public function indexData(Request $request): array
    {
        $sortable = ['id', 'status', 'created'];
        $sort = in_array($request->query('sort'), $sortable, true) ? $request->query('sort') : 'created';
        $dir = $request->query('dir') === 'asc' ? 'asc' : 'desc';
        $perPage = in_array((int) $request->query('per_page'), [10, 25, 50], true) ? (int) $request->query('per_page') : 10;

        $baseQuery = fn () => $request->user()->landingPageGenerations()
            ->where('layout_mode', LandingPageLayoutMode::Catalog);

        $query = $baseQuery()
            ->when($request->filled('status') && $request->query('status') !== 'all', function ($q) use ($request) {
                if ($request->query('status') === 'active') {
                    $q->whereNotIn('status', ['completed', 'failed']);
                } else {
                    $q->where('status', $request->query('status'));
                }
            })
            ->when($request->filled('q'), fn ($q) => $q->where(fn ($q2) => $q2
                ->where('brand_name', 'like', '%'.$request->query('q').'%')
                ->orWhere('brand_url', 'like', '%'.$request->query('q').'%')
            ))
            ->with(['landingPages.generation', 'publishedLandingPage.generation'])
            ->orderBy($sort === 'created' ? 'created_at' : $sort, $dir);

        $generations = $query->paginate($perPage)->withQueryString();
        $generations->getCollection()->transform(function (LandingPageGeneration $generation): LandingPageGeneration {
            $published = $generation->publishedLandingPage;
            $generation->setAttribute('published_summary', $published instanceof LandingPage ? $published->publishSummary() : null);
            $generation->setAttribute('analytics_url', $this->catalogAnalyticsUrl($generation, $published));

            return $generation;
        });

        $stats = [
            'all' => $baseQuery()->count(),
            'completed' => $baseQuery()->where('status', 'completed')->count(),
            'active' => $baseQuery()->whereNotIn('status', ['completed', 'failed'])->count(),
            'failed' => $baseQuery()->where('status', 'failed')->count(),
        ];

        return [
            'generations' => $generations,
            'stats' => $stats,
            'sort' => $sort,
            'dir' => $dir,
        ];
    }

    public function templatePreview(string $template, CatalogTemplateLocator $locator): Response
    {
        abort_unless(in_array($template, $locator->sharedTemplates(), true), 404);

        $html = $locator->previewDocument($template);

        abort_if($html === null, 404);

        return response($html, 200, [
            'Content-Type' => 'text/html; charset=UTF-8',
        ]);
    }

    public function downloadProductsJsonTemplate(CatalogProductImportValidator $validator): Response
    {
        return response($validator->exampleJson(), 200, [
            'Content-Type' => 'application/json; charset=UTF-8',
            'Content-Disposition' => 'attachment; filename="catalog-products-template.json"',
        ]);
    }

    public function show(LandingPageGeneration $generation): View|RedirectResponse
    {
        $this->authorizeCatalogGeneration($generation);

        if ($generation->isSetupIncomplete()) {
            return redirect()->route('catalog-landing-pages.create', ['generation' => $generation->id]);
        }

        $generation->load(['landingPages.generation', 'publishedLandingPage.generation']);

        return view('catalog-landing-pages.show', [
            'generation' => $generation,
            'displayStatus' => $generation->displayStatus(),
            'publishedAnalytics' => $generation->publishedAnalyticsSummary(),
            'variantAnalytics' => $generation->variantAnalyticsSummaries(),
        ]);
    }

    public function status(LandingPageGeneration $generation): JsonResponse
    {
        $this->authorizeCatalogGeneration($generation);

        $generation->load('landingPages.generation');

        return response()->json($this->statusPayload($generation));
    }

    public function preview(LandingPageGeneration $generation, LandingPage $landingPage): Response
    {
        $this->authorizeCatalogGeneration($generation);
        abort_unless($landingPage->landing_page_generation_id === $generation->id, 404);
        abort_unless($landingPage->isReady(), 404);

        return response($landingPage->renderedDocument(), 200, [
            'Content-Type' => 'text/html; charset=UTF-8',
        ]);
    }

    public function publish(LandingPageGeneration $generation, LandingPage $landingPage): RedirectResponse
    {
        $this->authorizeCatalogGeneration($generation);
        abort_unless($landingPage->landing_page_generation_id === $generation->id, 404);
        abort_unless($landingPage->isReady(), 404);

        $generation->update([
            'published_landing_page_id' => $landingPage->id,
            'published_at' => now(),
        ]);

        return back()->with('status', 'Published page updated. Analytics stay tied to each page variant.');
    }

    public function analytics(Request $request, LandingPageGeneration $generation, LandingPage $landingPage): View
    {
        $this->authorizeCatalogGeneration($generation);
        abort_unless($landingPage->landing_page_generation_id === $generation->id, 404);

        $sortable = ['id', 'created', 'clickouts'];
        $sort = in_array($request->query('sort'), $sortable, true) ? $request->query('sort') : 'created';
        $dir = $request->query('dir') === 'asc' ? 'asc' : 'desc';
        $perPage = in_array((int) $request->query('per_page'), [10, 25, 50], true) ? (int) $request->query('per_page') : 10;

        $visitsQuery = $landingPage->catalogVisits()->withCount('clickouts');

        $stats = [
            'visits' => (clone $visitsQuery)->count(),
            'clickouts' => $landingPage->catalogClickouts()->count(),
            'visits_with_clickouts' => (clone $visitsQuery)->whereHas('clickouts')->count(),
            'today' => (clone $visitsQuery)->where('created_at', '>=', now()->startOfDay())->count(),
            'week' => (clone $visitsQuery)->where('created_at', '>=', now()->subDays(7))->count(),
            'month' => (clone $visitsQuery)->where('created_at', '>=', now()->startOfMonth())->count(),
        ];

        $stats['clickout_rate'] = $stats['visits'] > 0
            ? round(($stats['visits_with_clickouts'] / $stats['visits']) * 100, 1)
            : 0.0;

        $visits = (clone $visitsQuery)
            ->when($request->filled('q'), function ($query) use ($request): void {
                $term = '%'.$request->query('q').'%';
                $query->where(function ($inner) use ($term): void {
                    $inner->where('query_params', 'like', $term)
                        ->orWhere('ip', 'like', $term);
                });
            })
            ->when($sort === 'clickouts', fn ($query) => $query->orderBy('clickouts_count', $dir))
            ->when($sort !== 'clickouts', fn ($query) => $query->orderBy($sort === 'created' ? 'created_at' : $sort, $dir))
            ->paginate($perPage)
            ->withQueryString();

        return view('catalog-landing-pages.analytics', [
            'generation' => $generation,
            'page' => $landingPage,
            'visits' => $visits,
            'stats' => $stats,
            'sort' => $sort,
            'dir' => $dir,
        ]);
    }

    public function download(LandingPageGeneration $generation, LandingPage $landingPage): Response
    {
        $this->authorizeCatalogGeneration($generation);
        abort_unless($landingPage->landing_page_generation_id === $generation->id, 404);
        abort_unless($landingPage->isReady(), 404);

        $filename = filled($landingPage->template)
            ? $landingPage->version->value.'-'.$landingPage->template.'.html'
            : $landingPage->version->value.'.html';

        return response($landingPage->renderedDocument(), 200, [
            'Content-Type' => 'text/html; charset=UTF-8',
            'Content-Disposition' => 'attachment; filename="'.$filename.'"',
        ]);
    }

    public function retry(LandingPageGeneration $generation, LandingPage $landingPage): RedirectResponse
    {
        $this->authorizeCatalogGeneration($generation);
        abort_unless($landingPage->landing_page_generation_id === $generation->id, 404);

        if (! $landingPage->canRetry()) {
            return redirect()->route('catalog-landing-pages.show', $generation);
        }

        $landingPage->update([
            'status' => 'queued',
            'title' => null,
            'html' => null,
            'css' => null,
            'js' => null,
            'prompt_used' => null,
            'failure_reason' => null,
        ]);

        $generation->update([
            'status' => 'queued',
            'failure_reason' => null,
        ]);

        try {
            GenerateCatalogLandingPagesJob::dispatch($generation->id, $landingPage->id);
        } catch (\Throwable $e) {
            // The job already records failure before rethrowing; with the sync
            // driver that rethrow would otherwise surface as a 500.
        }

        return redirect()->route('catalog-landing-pages.show', $generation);
    }

    public function destroy(LandingPageGeneration $generation): RedirectResponse
    {
        $this->authorizeCatalogGeneration($generation);

        $generation->delete();

        return redirect()->route('catalog-landing-pages.index');
    }

    public function updatePixels(Request $request, LandingPageGeneration $generation): RedirectResponse
    {
        $this->authorizeCatalogGeneration($generation);
        abort_unless($generation->canManagePixels(), 404);

        $validator = Validator::make($request->all(), [
            'google_conversion_send_to' => ['nullable', 'string', 'max:80', function (string $attribute, mixed $value, \Closure $fail): void {
                if (! is_string($value) || ! PromptLandingPagePixels::isGoogleSendTo($value)) {
                    $fail('Enter a Google Ads send_to id like AW-1234567890/AbCdEfGh.');
                }
            }],
            'meta_pixel_id' => ['nullable', 'string', 'max:20', function (string $attribute, mixed $value, \Closure $fail): void {
                if (! is_string($value) || ! PromptLandingPagePixels::isMetaPixelId($value)) {
                    $fail('Enter a Meta Pixel id made of digits only.');
                }
            }],
        ]);

        if ($validator->fails()) {
            return back()
                ->withErrors($validator)
                ->withInput()
                ->with('catalog_pixels_action', route('catalog-landing-pages.pixels.update', $generation));
        }

        $validated = $validator->validated();

        $generation->update([
            'google_conversion_send_to' => $validated['google_conversion_send_to'] ?? null,
            'meta_pixel_id' => $validated['meta_pixel_id'] ?? null,
        ]);

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

    public function updateAffiliateTracking(Request $request, LandingPageGeneration $generation): RedirectResponse
    {
        $this->authorizeCatalogGeneration($generation);
        abort_unless($generation->canManageAffiliateTracking(), 404);

        $validator = Validator::make($request->all(), [
            'admedia_campaign_id' => ['required', 'string', 'max:20', 'regex:/^\d+$/'],
            'affiliate_key' => ['required', 'string', 'max:40', function (string $attribute, mixed $value, \Closure $fail): void {
                if (! is_string($value) || ! CatalogAffiliateRegistry::isValidKey($value)) {
                    $fail('Select a valid affiliate.');
                }
            }],
        ], [], [
            'admedia_campaign_id' => 'Admedia campaign ID',
        ]);

        if ($validator->fails()) {
            return back()
                ->withErrors($validator)
                ->withInput()
                ->with('catalog_affiliate_action', route('catalog-landing-pages.affiliate.update', $generation));
        }

        $validated = $validator->validated();

        $generation->update([
            'admedia_campaign_id' => $validated['admedia_campaign_id'],
            'affiliate_key' => $validated['affiliate_key'],
        ]);

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

    private function authorizeCatalogGeneration(LandingPageGeneration $generation): void
    {
        abort_unless($generation->user_id === auth()->id(), 403);
        abort_unless($generation->layout_mode === LandingPageLayoutMode::Catalog, 404);
    }

    private function catalogAnalyticsUrl(LandingPageGeneration $generation, ?LandingPage $published): ?string
    {
        if ($published instanceof LandingPage) {
            return route('catalog-landing-pages.analytics', [$generation, $published]);
        }

        if ($generation->isSetupIncomplete()) {
            return null;
        }

        $pages = $generation->relationLoaded('landingPages')
            ? $generation->landingPages
            : $generation->landingPages()->orderBy('id')->get();

        $firstReady = $pages->first(fn (LandingPage $page): bool => $page->isReady());

        return $firstReady instanceof LandingPage
            ? route('catalog-landing-pages.analytics', [$generation, $firstReady])
            : null;
    }

    /**
     * @return array{status: string, failure_reason: ?string, done: bool, pages: list<array<string, mixed>>}
     */
    private function statusPayload(LandingPageGeneration $generation): array
    {
        $status = $generation->displayStatus();

        return [
            'status' => $status,
            'failure_reason' => $generation->failure_reason,
            'done' => in_array($status, ['completed', 'failed'], true),
            'pages' => $generation->landingPages->map(fn (LandingPage $page) => [
                'id' => $page->id,
                'status' => $page->status,
                'title' => $page->title,
                'failure_reason' => $page->failure_reason,
                'preview_url' => $page->isReady()
                    ? route('catalog-landing-pages.preview', [$generation, $page])
                    : null,
                'download_url' => $page->isReady()
                    ? route('catalog-landing-pages.download', [$generation, $page])
                    : null,
                'partner_preview_url' => $page->partnerPreviewUrl(),
                'public_url' => $page->publicUrl(),
                'retry_url' => $page->canRetry()
                    ? route('catalog-landing-pages.retry', [$generation, $page])
                    : null,
            ])->values()->all(),
        ];
    }
}
