<?php

declare(strict_types=1);

namespace App\Livewire;

use App\Exceptions\UploadRejectedException;
use App\Models\ServiceOrder;
use App\Services\Payments\PaymentService;
use App\Services\Services\ServiceOrderService;
use App\Services\Uploads\UploadService;
use Livewire\Attributes\Computed;
use Livewire\Component;
use Throwable;

/**
 * One mix or master order: pay for it, hand over the audio, collect the
 * finished file.
 *
 * The upload never passes through this component. The browser asks for a
 * signed URL, PUTs the file straight to storage, then calls back to say it is
 * done — at which point the server goes and checks. A 500 MB WAV would
 * otherwise hit upload_max_filesize, the memory limit and the request timeout
 * all at once on shared hosting.
 */
class ServiceOrderPage extends Component
{
    public ServiceOrder $order;

    public ?string $error = null;

    public ?string $notice = null;

    public string $sourceLink = '';

    public function mount(ServiceOrder $order): void
    {
        $this->order = $order;
    }

    #[Computed]
    public function owesMoney(): bool
    {
        return bccomp((string) $this->order->price_total, '0.00', 2) > 0
            && $this->order->paid_at === null;
    }

    #[Computed]
    public function sourceFiles()
    {
        return $this->order->sourceFiles()->latest()->get();
    }

    #[Computed]
    public function deliveries()
    {
        return $this->order->deliveries()->latest()->get();
    }

    /**
     * Hand the browser a URL it can upload to directly.
     *
     * The key is generated server-side and returned with the URL; the client
     * never chooses where its file lands.
     */
    public function signUpload(string $filename, string $mime, int $bytes, UploadService $uploads): array
    {
        $this->error = null;

        try {
            return $uploads->sign('source_audio', $filename, $mime, $bytes)->toArray();
        } catch (UploadRejectedException $e) {
            $this->error = $e->getMessage();

            return ['error' => $e->getMessage()];
        }
    }

    /**
     * The browser says the upload finished. Verify before believing it.
     */
    public function confirmUpload(
        string $key,
        string $filename,
        string $mime,
        UploadService $uploads,
        ServiceOrderService $orders,
    ): void {
        $this->error = null;

        try {
            $uploads->confirm($this->order, 'source_audio', $key, $filename, $mime, auth()->user());
        } catch (UploadRejectedException $e) {
            $this->error = $e->getMessage();

            return;
        } catch (Throwable $e) {
            report($e);
            $this->error = 'Something went wrong saving that file. Please try again.';

            return;
        }

        // Having the audio may be the thing that starts the turnaround clock.
        $orders->sourceReceived($this->order);

        $this->order->refresh();
        $this->notice = 'Got it. We have everything we need.';
        unset($this->sourceFiles);
    }

    public function addLink(UploadService $uploads, ServiceOrderService $orders): void
    {
        $this->error = null;

        try {
            $uploads->recordLink($this->order, 'source_audio', trim($this->sourceLink), auth()->user());
        } catch (UploadRejectedException $e) {
            $this->error = $e->getMessage();

            return;
        }

        $orders->sourceReceived($this->order);

        $this->order->refresh();
        $this->sourceLink = '';
        $this->notice = 'Link saved.';
        unset($this->sourceFiles);
    }

    public function payByCard(PaymentService $payments)
    {
        $this->error = null;

        try {
            $payment = $payments->startServiceCardPayment($this->order);
        } catch (Throwable $e) {
            report($e);
            $this->error = 'We could not open the payment page. Please contact the studio.';

            return null;
        }

        return $this->redirect($payment->checkout_url);
    }

    /** A short-lived link to a finished master. */
    public function download(int $uploadId, UploadService $uploads)
    {
        $upload = $this->order->deliveries()->findOrFail($uploadId);

        return $this->redirect($uploads->readUrl($upload));
    }

    public function render()
    {
        return view('livewire.service-order')->layout('layouts.site');
    }
}
