<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Http\Requests\Warranty\RenewWarrantyRequest;
use App\Http\Requests\Warranty\StoreWarrantyRequest;
use App\Http\Requests\Warranty\UpdateWarrantyRequest;
use App\Models\Asset;
use App\Models\AssetWarranty;
use App\Services\WarrantyService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\StreamedResponse;

/**
 * Coverage periods, nested under the asset they belong to — there is no
 * standalone "warranty" URL a user reaches without going through an asset
 * first, matching how the asset detail page is where every warranty action
 * is actually offered.
 */
class AssetWarrantyController extends Controller
{
    public function __construct(
        private readonly WarrantyService $warranties,
    ) {}

    public function create(Asset $asset): View
    {
        return view('assets.warranties.create', [
            'asset' => $asset,
            'warranty' => new AssetWarranty([
                'start_date' => now(),
                'end_date' => now()->addYear(),
            ]),
            'isFirstWarranty' => ! $asset->warranties()->exists(),
            'providerSuggestions' => $this->providerSuggestions(),
        ]);
    }

    public function store(StoreWarrantyRequest $request, Asset $asset): RedirectResponse
    {
        $warranty = $this->warranties->create($asset, $request->validated());

        return redirect()
            ->route('assets.show', $asset)
            ->with('success', __(':type added.', ['type' => $warranty->type->label()]));
    }

    public function edit(Asset $asset, AssetWarranty $warranty): View
    {
        $this->ensureBelongsTo($asset, $warranty);

        return view('assets.warranties.edit', [
            'asset' => $asset,
            'warranty' => $warranty,
            'providerSuggestions' => $this->providerSuggestions(),
        ]);
    }

    public function update(UpdateWarrantyRequest $request, Asset $asset, AssetWarranty $warranty): RedirectResponse
    {
        $this->ensureBelongsTo($asset, $warranty);

        $this->warranties->update($warranty, $request->validated());

        return redirect()
            ->route('assets.show', $asset)
            ->with('success', __(':type updated.', ['type' => $warranty->type->label()]));
    }

    public function renewForm(Asset $asset, AssetWarranty $warranty): View
    {
        $this->ensureBelongsTo($asset, $warranty);

        return view('assets.warranties.renew', [
            'asset' => $asset,
            'warranty' => $warranty,
        ]);
    }

    public function renew(RenewWarrantyRequest $request, Asset $asset, AssetWarranty $warranty): RedirectResponse
    {
        $this->ensureBelongsTo($asset, $warranty);

        $renewed = $this->warranties->renew($warranty, $request->validated());

        return redirect()
            ->route('assets.show', $asset)
            ->with('success', __(':type renewed, now covered to :date.', [
                'type' => $renewed->type->label(),
                'date' => $renewed->end_date->format('d M Y'),
            ]));
    }

    public function destroy(Asset $asset, AssetWarranty $warranty): RedirectResponse
    {
        $this->ensureBelongsTo($asset, $warranty);
        $this->authorize('delete', $warranty);

        $label = $warranty->type->label();

        $this->warranties->delete($warranty);

        return redirect()
            ->route('assets.show', $asset)
            ->with('success', __(':type deleted. You can restore it from the deleted list.', ['type' => $label]));
    }

    public function restore(Asset $asset, AssetWarranty $warranty): RedirectResponse
    {
        $this->ensureBelongsTo($asset, $warranty);

        $warranty->restore();

        return redirect()
            ->route('assets.show', $asset)
            ->with('success', __(':type restored.', ['type' => $warranty->type->label()]));
    }

    public function document(Asset $asset, AssetWarranty $warranty): StreamedResponse
    {
        $this->ensureBelongsTo($asset, $warranty);

        abort_if($warranty->document_path === null, 404);

        return Storage::disk('local')->download(
            $warranty->document_path,
            $warranty->document_original_name ?? 'warranty-document',
        );
    }

    /**
     * Routes are defined manually rather than via `Route::resource()`, so
     * nested scoping (refusing a `{warranty}` that belongs to a different
     * `{asset}`) is not automatic — this is the one check every action
     * taking both parameters needs.
     */
    private function ensureBelongsTo(Asset $asset, AssetWarranty $warranty): void
    {
        abort_unless($warranty->asset_id === $asset->id, 404);
    }

    /**
     * @return Collection<int, string>
     */
    private function providerSuggestions(): Collection
    {
        return AssetWarranty::query()
            ->whereNotNull('provider')
            ->where('provider', '!=', '')
            ->distinct()
            ->orderBy('provider')
            ->pluck('provider');
    }
}
