<?php

namespace App\Services;

use App\Exceptions\TokenExpiredException;
use App\Mail\SubscriptionConfirmationMail;
use App\Models\EmailSubscriber;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Mail;
use Symfony\Component\HttpFoundation\StreamedResponse;

class EmailSubscriptionService
{
    /**
     * Subscribe a new visitor.
     * Creates an EmailSubscriber with is_active=false, auto-UUID token (via model booted()),
     * subscribed_at=now, and dispatches the confirmation email.
     *
     * Requirements: 7.2, 7.6, 8.5
     */
    public function subscribe(array $data, string $ip): EmailSubscriber
    {
        $subscriber = EmailSubscriber::create([
            'email'          => $data['email'],
            'name'           => $data['name'] ?? null,
            'subscribed_at'  => now(),
            'is_active'      => false,
            'ip_address'     => $ip,
            // confirmation_token is auto-generated by the model's booted() hook
        ]);

        Mail::to($subscriber->email)->queue(new SubscriptionConfirmationMail($subscriber));

        return $subscriber;
    }

    /**
     * Confirm a subscriber's email via their confirmation token.
     * Checks 48-hour expiry window (Req 7.8), sets confirmed_at=now and is_active=true (Req 7.7).
     *
     * Requirements: 7.7, 7.8, 7.9
     *
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException if token not found
     * @throws TokenExpiredException if token is older than 48 hours
     */
    public function confirm(string $token): EmailSubscriber
    {
        $subscriber = EmailSubscriber::where('confirmation_token', $token)->firstOrFail();

        // Req 7.8: token expires after 48 hours
        if ($subscriber->subscribed_at->diffInHours(now()) > 48) {
            throw new TokenExpiredException();
        }

        $subscriber->update([
            'confirmed_at' => now(),
            'is_active'    => true,
        ]);

        return $subscriber->fresh();
    }

    /**
     * Unsubscribe a subscriber via their confirmation token.
     * Sets unsubscribed_at=now and is_active=false.
     *
     * Requirements: 7.10
     *
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException if token not found
     */
    public function unsubscribe(string $token): EmailSubscriber
    {
        $subscriber = EmailSubscriber::where('confirmation_token', $token)->firstOrFail();

        $subscriber->update([
            'unsubscribed_at' => now(),
            'is_active'       => false,
        ]);

        return $subscriber->fresh();
    }

    /**
     * Get all active confirmed subscribers.
     * Active = is_active true + not unsubscribed. Confirmed = confirmed_at not null.
     *
     * Requirements: 9.3
     */
    public function getActiveSubscribers(): Collection
    {
        return EmailSubscriber::active()->confirmed()->get();
    }

    /**
     * Stream a CSV export of all active confirmed subscribers.
     *
     * Requirements: 9.3
     */
    public function exportCsv(): StreamedResponse
    {
        $subscribers = $this->getActiveSubscribers();

        $headers = [
            'Content-Type'        => 'text/csv',
            'Content-Disposition' => 'attachment; filename="email-subscribers-' . now()->format('Y-m-d') . '.csv"',
        ];

        return response()->stream(function () use ($subscribers) {
            $handle = fopen('php://output', 'w');

            // CSV header row
            fputcsv($handle, ['Email', 'Name', 'Subscribed At', 'Confirmed At']);

            foreach ($subscribers as $subscriber) {
                fputcsv($handle, [
                    $subscriber->email,
                    $subscriber->name ?? '',
                    $subscriber->subscribed_at?->format('Y-m-d H:i:s'),
                    $subscriber->confirmed_at?->format('Y-m-d H:i:s'),
                ]);
            }

            fclose($handle);
        }, 200, $headers);
    }
}
