<?php

namespace App\Http\Controllers;

use App\Mail\ContactConfirmation;
use App\Mail\ContactRequest;
use App\Models\BlogPost;
use App\Models\CheckoutSession;
use App\Models\Event;
use App\Models\Package;
use App\Models\PackagePurchase;
use App\Models\TenantPackage;
use App\Services\Checkout\CheckoutSessionService;
use App\Services\Coupons\CouponService;
use App\Services\GiftVouchers\GiftVoucherCheckoutService;
use App\Services\Paddle\PaddleCheckoutService;
use App\Support\CheckoutRequestContext;
use App\Support\CheckoutRoutes;
use App\Support\Concerns\PresentsPackages;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\Autolink\AutolinkExtension;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\Strikethrough\StrikethroughExtension;
use League\CommonMark\Extension\Table\TableExtension;
use League\CommonMark\Extension\TaskList\TaskListExtension;
use League\CommonMark\MarkdownConverter;

class MarketingController extends Controller
{
    use PresentsPackages;

    private ?MarkdownConverter $markdownConverter = null;

    public function __construct(
        private readonly CheckoutSessionService $checkoutSessions,
        private readonly PaddleCheckoutService $paddleCheckout,
        private readonly CouponService $coupons,
        private readonly GiftVoucherCheckoutService $giftVouchers,
    ) {}

    public function index()
    {
        $packages = Package::where('type', 'endcustomer')
            ->orderBy('price')
            ->get()
            ->map(fn (Package $package) => $this->presentPackage($package))
            ->values()
            ->all();

        return Inertia::render('marketing/Home', compact('packages'));
    }

    public function contact(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|email|max:255',
            'message' => 'required|string|max:1000',
            'nickname' => 'present|size:0',
        ]);

        $locale = app()->getLocale();
        $contactAddress = config('mail.contact_address', config('mail.from.address'));

        if (! $contactAddress) {
            throw ValidationException::withMessages([
                'email' => __('marketing.contact.error_recipient_missing', [], $locale) ?: 'Anfrage derzeit nicht möglich. Bitte später erneut versuchen.',
            ]);
        }

        try {
            Mail::to($contactAddress)
                ->locale($locale)
                ->send(new ContactRequest(
                    name: $request->name,
                    email: $request->email,
                    messageBody: $request->message,
                ));

            Mail::to($request->email)
                ->locale($locale)
                ->queue(new ContactConfirmation($request->name));
        } catch (\Throwable $exception) {
            Log::error('Contact form mail failed', [
                'error' => $exception->getMessage(),
                'code' => $exception->getCode(),
            ]);

            throw ValidationException::withMessages([
                'email' => __('marketing.contact.error_send_failed', [], $locale) ?: 'Nachricht konnte nicht gesendet werden. Bitte versuche es später erneut.',
            ]);
        }

        return redirect()
            ->back()
            ->with('success', __('marketing.contact.success', [], $locale));
    }

    public function contactView(Request $request)
    {
        $locale = \App\Support\LocaleConfig::canonicalize((string) ($request->route('locale') ?? app()->getLocale()));
        $secondSegment = $request->segment(2);
        $slug = $secondSegment ? '/'.trim((string) $secondSegment, '/') : '/';

        if ($locale === 'en' && $slug === '/kontakt') {
            return redirect()->route('marketing.contact', [
                'locale' => $request->route('locale') ?? $locale,
            ], 301);
        }

        if ($locale === 'de' && $slug === '/contact') {
            return redirect()->route('kontakt', [
                'locale' => $request->route('locale') ?? $locale,
            ], 301);
        }

        return Inertia::render('marketing/Kontakt');
    }

    public function giftVouchers()
    {
        $tiers = $this->giftVouchers->tiers();

        return Inertia::render('marketing/GiftVoucher', [
            'tiers' => $tiers,
        ]);
    }

    /**
     * Handle package purchase flow.
     */
    public function buyPackages(Request $request, string $locale, $packageId)
    {
        Log::info('Buy packages called', ['auth' => Auth::check(), 'locale' => $locale, 'package_id' => $packageId]);
        $package = Package::findOrFail($packageId);

        $requiresWaiver = (bool) ($package->activates_immediately ?? true);

        $request->validate([
            'accepted_terms' => ['sometimes', 'boolean', 'accepted'],
            'accepted_waiver' => ['sometimes', 'boolean'],
        ]);

        $couponCode = $this->rememberCouponFromRequest($request, $package);

        if (! Auth::check()) {
            return redirect()->to(CheckoutRoutes::wizardUrl($package->id, $locale))
                ->with('message', __('marketing.packages.register_required'));
        }

        $user = Auth::user();
        if (! $user->email_verified_at) {
            return redirect()->route('verification.notice')
                ->with('message', __('auth.verification_required'));
        }

        $tenant = $user->tenant;
        if (! $tenant) {
            abort(500, 'Tenant not found');
        }

        if ($package->price == 0) {
            TenantPackage::updateOrCreate(
                [
                    'tenant_id' => $tenant->id,
                    'package_id' => $package->id,
                ],
                [
                    'price' => $package->price,
                    'active' => true,
                    'purchased_at' => now(),
                    'expires_at' => now()->addYear(),
                ]
            );

            PackagePurchase::create([
                'tenant_id' => $tenant->id,
                'package_id' => $package->id,
                'provider' => 'free',
                'provider_id' => 'free',
                'price' => $package->price,
                'type' => $package->type === 'endcustomer' ? 'endcustomer_event' : 'reseller_subscription',
                'purchased_at' => now(),
                'refunded' => false,
            ]);

            return redirect('/event-admin')->with('success', __('marketing.packages.free_assigned'));
        }

        if (! $package->paddle_price_id) {
            Log::warning('Package missing Paddle price id', ['package_id' => $package->id]);

            return redirect()->route('packages', [
                'locale' => app()->getLocale(),
                'highlight' => $package->slug,
            ])
                ->with('error', __('marketing.packages.paddle_not_configured'));
        }

        $session = $this->checkoutSessions->createOrResume($user, $package, array_merge(
            CheckoutRequestContext::fromRequest($request),
            [
                'tenant' => $tenant,
            ]
        ));

        $this->checkoutSessions->selectProvider($session, CheckoutSession::PROVIDER_PADDLE);

        $now = now();

        $session->forceFill([
            'accepted_terms_at' => $request->boolean('accepted_terms') ? $now : null,
            'accepted_privacy_at' => $request->boolean('accepted_terms') ? $now : null,
            'accepted_withdrawal_notice_at' => $request->boolean('accepted_terms') ? $now : null,
            'digital_content_waiver_at' => $requiresWaiver && $request->boolean('accepted_waiver') ? $now : null,
            'legal_version' => $this->resolveLegalVersion(),
        ])->save();

        $appliedDiscountId = null;

        if ($couponCode) {
            try {
                $preview = $this->coupons->preview($couponCode, $package, $tenant);
                $this->checkoutSessions->applyCoupon($session, $preview['coupon'], $preview['pricing']);
                $appliedDiscountId = $preview['coupon']->paddle_discount_id;
                $request->session()->forget('marketing.checkout.coupon');
            } catch (ValidationException $exception) {
                $request->session()->flash('coupon_error', $exception->errors()['code'][0] ?? __('marketing.coupon.errors.generic'));
            }
        }

        $checkout = $this->paddleCheckout->createCheckout($tenant, $package, [
            'success_url' => route('marketing.success', [
                'locale' => app()->getLocale(),
                'packageId' => $package->id,
            ]),
            'return_url' => route('packages', [
                'locale' => app()->getLocale(),
                'highlight' => $package->slug,
            ]),
            'metadata' => [
                'checkout_session_id' => $session->id,
                'coupon_code' => $couponCode,
                'legal_version' => $session->legal_version,
                'accepted_terms' => (bool) $session->accepted_terms_at,
                'accepted_waiver' => $requiresWaiver && (bool) $session->digital_content_waiver_at,
            ],
            'discount_id' => $appliedDiscountId,
        ]);

        $session->forceFill([
            'paddle_checkout_id' => $checkout['id'] ?? $session->paddle_checkout_id,
            'provider_metadata' => array_merge($session->provider_metadata ?? [], array_filter([
                'paddle_checkout_id' => $checkout['id'] ?? null,
                'paddle_checkout_url' => $checkout['checkout_url'] ?? null,
                'paddle_expires_at' => $checkout['expires_at'] ?? null,
            ])),
        ])->save();

        $redirectUrl = $checkout['checkout_url'] ?? null;

        if (! $redirectUrl) {
            throw ValidationException::withMessages([
                'paddle' => __('marketing.packages.paddle_checkout_failed'),
            ]);
        }

        return redirect()->away($redirectUrl);
    }

    public function success(Request $request, $packageId = null)
    {
        if (Auth::check() && Auth::user()->email_verified_at) {
            return redirect('/event-admin')->with('success', __('marketing.success.welcome'));
        }

        return Inertia::render('marketing/Success', compact('packageId'));
    }

    protected function rememberCouponFromRequest(Request $request, Package $package): ?string
    {
        $input = Str::upper(trim((string) $request->input('coupon')));

        if ($input !== '') {
            $request->session()->put('marketing.checkout.coupon', [
                'package_id' => $package->id,
                'code' => $input,
            ]);

            return $input;
        }

        if ($request->has('coupon')) {
            $request->session()->forget('marketing.checkout.coupon');

            return null;
        }

        $stored = $request->session()->get('marketing.checkout.coupon');

        if ($stored && (int) ($stored['package_id'] ?? 0) === (int) $package->id) {
            return $stored['code'] ?? null;
        }

        return null;
    }

    public function blogIndex(Request $request, string $locale)
    {
        $locale = $locale ?: app()->getLocale();
        $query = BlogPost::query()
            ->with('author')
            ->whereHas('category', function ($query) {
                $query->where('slug', 'blog');
            });

        $query->where('is_published', true)
            ->whereNotNull('published_at')
            ->where('published_at', '<=', now());

        // Removed translation filter for now

        $posts = $query->orderBy('published_at', 'desc')
            ->paginate(4)
            ->through(function (BlogPost $post) use ($locale) {
                $excerpt = $post->getTranslation('excerpt', $locale) ?? $post->getTranslation('excerpt', 'de') ?? '';

                return [
                    'id' => $post->id,
                    'slug' => $post->slug,
                    'title' => $post->getTranslation('title', $locale) ?? $post->getTranslation('title', 'de') ?? '',
                    'excerpt' => $excerpt,
                    'excerpt_html' => $this->convertMarkdownToHtml($excerpt),
                    'featured_image' => $post->featured_image ?? $post->banner_url ?? null,
                    'published_at' => optional($post->published_at)->toDateString(),
                    'author' => $post->author ? ['name' => $post->author->name] : null,
                ];
            });

        $postsArray = $posts->toArray();
        $postsArray['links'] = array_map(function (array $link) use ($locale) {
            return [
                'url' => $link['url'],
                'label' => $this->localizePaginationLabel($link['label'] ?? '', $locale),
                'active' => (bool) ($link['active'] ?? false),
            ];
        }, $postsArray['links'] ?? []);

        return Inertia::render('marketing/Blog', [
            'posts' => $postsArray,
        ]);
    }

    public function blogShow(string $locale, string $slug)
    {
        $locale = $locale ?: app()->getLocale();
        $postModel = BlogPost::query()
            ->with('author')
            ->whereHas('category', function ($query) {
                $query->where('slug', 'blog');
            })
            ->where('slug', $slug)
            ->where('is_published', true)
            ->whereNotNull('published_at')
            ->where('published_at', '<=', now())
            // Removed translation filter for now
            ->firstOrFail();

        // Transform to array with translated strings for the current locale
        $markdown = $postModel->getTranslation('content', $locale) ?? $postModel->getTranslation('content', 'de') ?? '';
        $excerpt = $postModel->getTranslation('excerpt', $locale) ?? $postModel->getTranslation('excerpt', 'de') ?? '';
        $contentHtml = $this->convertMarkdownToHtml($markdown);
        [$contentHtmlWithIds, $headings] = $this->decorateHeadings($contentHtml);

        $post = [
            'id' => $postModel->id,
            'title' => $postModel->getTranslation('title', $locale) ?? $postModel->getTranslation('title', 'de') ?? '',
            'excerpt' => $excerpt,
            'excerpt_html' => $this->convertMarkdownToHtml($excerpt),
            'content' => $markdown,
            'content_html' => $contentHtmlWithIds,
            'headings' => $headings,
            'featured_image' => $postModel->featured_image ?? $postModel->banner_url ?? null,
            'published_at' => $postModel->published_at->toDateString(),
            'slug' => $postModel->slug,
            'url' => route('blog.show', ['locale' => $locale, 'slug' => $postModel->slug], absolute: true),
            'author' => $postModel->author ? [
                'name' => $postModel->author->name,
            ] : null,
            'previous_post' => $this->presentAdjacentPost($postModel, $locale, direction: 'previous'),
            'next_post' => $this->presentAdjacentPost($postModel, $locale, direction: 'next'),
        ];

        return Inertia::render('marketing/BlogShow', compact('post'));
    }

    public function howItWorks()
    {
        return Inertia::render('marketing/HowItWorks');
    }

    public function demo()
    {
        $joinToken = optional(Event::firstWhere('slug', 'demo-wedding-2025'))
            ?->joinTokens()
            ->latest('id')
            ->first();

        $demoToken = null;

        if ($joinToken) {
            if (! empty($joinToken->token_encrypted)) {
                try {
                    $demoToken = Crypt::decryptString($joinToken->token_encrypted);
                } catch (\Throwable $exception) {
                    Log::warning('Failed to decrypt demo join token', [
                        'token_id' => $joinToken->id,
                        'exception' => $exception->getMessage(),
                    ]);
                }
            }

            if (! $demoToken) {
                $demoToken = $joinToken->metadata['plain_token'] ?? null;
            }
        }

        return Inertia::render('marketing/Demo', [
            'demoToken' => $demoToken,
        ]);
    }

    public function packagesIndex()
    {
        $endcustomerPackages = Package::where('type', 'endcustomer')
            ->orderBy('price')
            ->get()
            ->map(fn (Package $package) => $this->presentPackage($package))
            ->values()
            ->all();

        $resellerPackages = Package::where('type', 'reseller')
            ->orderBy('price')
            ->get()
            ->map(fn (Package $package) => $this->presentPackage($package))
            ->values()
            ->all();

        return Inertia::render('marketing/Packages', [
            'endcustomerPackages' => $endcustomerPackages,
            'resellerPackages' => $resellerPackages,
        ]);
    }

    public function occasionsType(Request $request, string $locale, string $type)
    {
        Log::info('OccasionsType hit', [
            'type' => $type,
            'locale' => $locale,
            'url' => request()->fullUrl(),
            'route' => request()->route()->getName(),
            'isInertia' => request()->header('X-Inertia'),
        ]);

        $normalized = strtolower($type);
        $typeMap = [
            'hochzeit' => 'hochzeit',
            'wedding' => 'hochzeit',
            'geburtstag' => 'geburtstag',
            'birthday' => 'geburtstag',
            'firmenevent' => 'firmenevent',
            'corporate-event' => 'firmenevent',
            'konfirmation' => 'konfirmation',
            'confirmation' => 'konfirmation',
        ];

        if (! array_key_exists($normalized, $typeMap)) {
            Log::warning('Invalid occasion type accessed', ['type' => $type]);
            abort(404, 'Invalid occasion type');
        }

        $baseSlug = $typeMap[$normalized];

        $canonical = [
            'hochzeit' => [
                'de' => 'hochzeit',
                'en' => 'wedding',
            ],
            'geburtstag' => [
                'de' => 'geburtstag',
                'en' => 'birthday',
            ],
            'firmenevent' => [
                'de' => 'firmenevent',
                'en' => 'corporate-event',
            ],
            'konfirmation' => [
                'de' => 'konfirmation',
                'en' => 'confirmation',
            ],
        ];

        $canonicalSlug = $canonical[$baseSlug][$locale] ?? $baseSlug;
        $currentSlug = strtolower($type);

        if ($currentSlug !== $canonicalSlug) {
            $routeName = $locale === 'en' ? 'occasions.type' : 'anlaesse.type';

            return redirect()->route($routeName, [
                'locale' => $locale,
                'type' => $canonicalSlug,
            ], 301);
        }

        return Inertia::render('marketing/Occasions', [
            'type' => $baseSlug,
            'requestedType' => $normalized,
        ]);
    }

    private function localizePaginationLabel(?string $label, string $locale): string
    {
        $decoded = trim(html_entity_decode(strip_tags($label ?? '')));

        if ($decoded === '') {
            return '';
        }

        if ($decoded === '...') {
            return '…';
        }

        $normalized = Str::lower($decoded);

        if (Str::contains($normalized, ['previous', 'vorherige', 'zurück'])) {
            return __('marketing.blog.pagination.previous', [], $locale);
        }

        if (Str::contains($normalized, ['next', 'weiter', 'nächste'])) {
            return __('marketing.blog.pagination.next', [], $locale);
        }

        return $decoded;
    }

    private function markdownConverter(): MarkdownConverter
    {
        if (! $this->markdownConverter instanceof MarkdownConverter) {
            $environment = new Environment;
            $environment->addExtension(new CommonMarkCoreExtension);
            $environment->addExtension(new TableExtension);
            $environment->addExtension(new AutolinkExtension);
            $environment->addExtension(new StrikethroughExtension);
            $environment->addExtension(new TaskListExtension);

            $this->markdownConverter = new MarkdownConverter($environment);
        }

        return $this->markdownConverter;
    }

    private function convertMarkdownToHtml(?string $markdown): string
    {
        if ($markdown === null || trim((string) $markdown) === '') {
            return '';
        }

        return (string) $this->markdownConverter()->convert($markdown);
    }

    private function decorateHeadings(string $html): array
    {
        $headings = [];
        $usedSlugs = [];

        $updatedHtml = preg_replace_callback('/<h([2-3])>(.*?)<\/h\1>/', function ($matches) use (&$headings, &$usedSlugs) {
            $level = (int) $matches[1];
            $text = trim(strip_tags($matches[2]));
            $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5);

            if ($text === '') {
                return $matches[0];
            }

            $baseSlug = Str::slug($text) ?: 'section';
            $slug = $baseSlug;
            $iterator = 1;

            while (in_array($slug, $usedSlugs, true)) {
                $slug = $baseSlug.'-'.(++$iterator);
            }

            $usedSlugs[] = $slug;
            $headings[] = [
                'text' => $text,
                'slug' => $slug,
                'level' => $level,
            ];

            return sprintf('<h%s id="%s">%s</h%s>', $level, $slug, $matches[2], $level);
        }, $html) ?: $html;

        return [$updatedHtml, $headings];
    }

    private function presentAdjacentPost(BlogPost $current, string $locale, string $direction): ?array
    {
        $operator = $direction === 'previous' ? '<' : '>';
        $orderDirection = $direction === 'previous' ? 'desc' : 'asc';

        $neighbor = BlogPost::query()
            ->with('author')
            ->whereHas('category', function ($query) {
                $query->where('slug', 'blog');
            })
            ->where('is_published', true)
            ->whereNotNull('published_at')
            ->where('published_at', $operator, $current->published_at)
            ->orderBy('published_at', $orderDirection)
            ->first();

        if (! $neighbor) {
            return null;
        }

        $excerpt = $neighbor->getTranslation('excerpt', $locale) ?? $neighbor->getTranslation('excerpt', 'de') ?? '';

        return [
            'slug' => $neighbor->slug,
            'title' => $neighbor->getTranslation('title', $locale) ?? $neighbor->getTranslation('title', 'de') ?? '',
            'excerpt' => $excerpt,
            'excerpt_html' => $this->convertMarkdownToHtml($excerpt),
        ];
    }

    protected function resolveLegalVersion(): string
    {
        return config('app.legal_version', now()->toDateString());
    }
}
