<?php

namespace App\Services;

use App\Models\SelfPaced\BookPurchase;
use App\Models\SelfPaced\Course;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
use Symfony\Component\HttpFoundation\StreamedResponse;

/**
 * BookService
 *
 * Handles course book uploads, purchase tracking, signed URL generation,
 * and downloads.
 *
 * Requirements: 9.3, 9.4, 9.8, 9.9, 9.10, 9.11
 */
class BookService
{
    const STORAGE_PATH = 'self-paced/books';
    const SIGNED_URL_TTL_HOURS = 24;
    const MAX_FILE_SIZE = 52428800; // 50 MB

    /**
     * Upload a book PDF to a course
     *
     * @param Course $course
     * @param UploadedFile $file
     * @return Course
     * @throws \Exception
     */
    public function uploadBook(Course $course, UploadedFile $file): Course
    {
        // Validate file type
        if ($file->getMimeType() !== 'application/pdf') {
            throw new \Exception('Only PDF files are allowed.');
        }

        // Validate file size
        if ($file->getSize() > self::MAX_FILE_SIZE) {
            throw new \Exception('File size exceeds 50 MB limit.');
        }

        // Delete old book if exists
        if ($course->book_path) {
            Storage::disk('public')->delete($course->book_path);
        }

        // Store new book
        $path = Storage::disk('public')->putFile(
            self::STORAGE_PATH,
            $file
        );

        if (!$path) {
            throw new \Exception('Failed to upload book file.');
        }

        // Update course
        $course->update([
            'book_path' => $path,
            'book_file_name' => $file->getClientOriginalName(),
            'book_file_size' => $file->getSize(),
        ]);

        return $course;
    }

    /**
     * Delete a course book
     *
     * @param Course $course
     * @return void
     */
    public function deleteBook(Course $course): void
    {
        if ($course->book_path) {
            Storage::disk('public')->delete($course->book_path);
        }

        $course->update([
            'book_path' => null,
            'book_file_name' => null,
            'book_file_size' => null,
        ]);
    }

    /**
     * Record a book purchase and generate a signed download URL
     *
     * @param Course $course
     * @param string $email Customer email
     * @param string $paymentReference Paystack/payment reference
     * @param float $amountPaid Amount paid
     * @param string $currency Currency code
     * @param int|null $userId Optional user ID if logged in
     * @return array{purchase: BookPurchase, downloadUrl: string}
     */
    public function recordPurchaseAndGenerateUrl(
        Course $course,
        string $email,
        string $paymentReference,
        float $amountPaid,
        string $currency,
        ?int $userId = null
    ): array {
        $purchase = BookPurchase::create([
            'course_id' => $course->id,
            'user_id' => $userId,
            'email' => $email,
            'payment_reference' => $paymentReference,
            'payment_method' => 'paystack',
            'amount_paid' => $amountPaid,
            'currency' => $currency,
            'payment_date' => now(),
        ]);

        $downloadUrl = $this->generateDownloadUrl($purchase);

        return [
            'purchase' => $purchase,
            'downloadUrl' => $downloadUrl,
        ];
    }

    /**
     * Generate a signed download URL for a book purchase
     *
     * @param BookPurchase $purchase
     * @return string
     */
    public function generateDownloadUrl(BookPurchase $purchase): string
    {
        return URL::temporarySignedRoute(
            'self-paced.learner.book.download',
            now()->addHours(self::SIGNED_URL_TTL_HOURS),
            ['course' => $purchase->course_id, 'purchase' => $purchase->id]
        );
    }

    /**
     * Validate and retrieve a purchase from a signed URL token
     *
     * @param string $token Signed URL token (not used directly, URL validation handled by route)
     * @param int $purchaseId
     * @return BookPurchase
     * @throws \Exception
     */
    public function validateDownloadUrl(string $token, int $purchaseId): BookPurchase
    {
        $purchase = BookPurchase::find($purchaseId);

        if (!$purchase) {
            throw new \Exception('Purchase not found.', 404);
        }

        // Check if purchase has a valid book
        if (!$purchase->course->hasBook()) {
            throw new \Exception('Book no longer available.', 410);
        }

        return $purchase;
    }

    /**
     * Stream a book PDF to the browser
     *
     * @param Course $course
     * @return StreamedResponse
     * @throws \Exception
     */
    public function streamBook(Course $course): StreamedResponse
    {
        if (!$course->hasBook() || !Storage::disk('public')->exists($course->book_path)) {
            abort(410, 'Book file not found.');
        }

        return Storage::disk('public')->download(
            $course->book_path,
            $course->book_file_name ?? 'course-book.pdf',
            ['Content-Type' => 'application/pdf']
        );
    }

    /**
     * Dispatch download email to buyer
     *
     * @param BookPurchase $purchase
     * @param string $downloadUrl
     * @return void
     */
    public function dispatchDownloadEmail(BookPurchase $purchase, string $downloadUrl): void
    {
        // TODO: Dispatch BookDownloadMail
    }
}
