<?php

namespace App\Http\Controllers;

use App\Enums\PackageType;
use App\Http\Requests\EstimatePackageRequest;
use App\Http\Requests\StorePackageRequest;
use App\Http\Requests\UpdatePackageRequest;
use App\Models\Package;
use App\Models\Product;
use App\Services\AuditService;
use App\Services\PackageService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;

class PackageController extends Controller
{
    public function index(Request $request): View
    {
        $this->authorize('viewAny', Package::class);

        $packages = Package::query()
            ->withCount('items')
            ->when($request->filled('search'), function ($query) use ($request): void {
                $search = $request->string('search')->toString();
                $query->where(function ($builder) use ($search): void {
                    $builder->where('name', 'like', "%{$search}%")
                        ->orWhere('code', 'like', "%{$search}%");
                });
            })
            ->when($request->filled('status'), fn ($query) => $query->where('status', $request->string('status')))
            ->when($request->filled('category'), fn ($query) => $query->where('category', $request->string('category')))
            ->latest()
            ->paginate(15)
            ->withQueryString();

        return view('packages.index', compact('packages'));
    }

    public function create(): View
    {
        $this->authorize('create', Package::class);

        $products = Product::query()->where('is_active', true)->orderBy('name')->get();
        $packageTypes = PackageType::cases();

        return view('packages.create', compact('products', 'packageTypes'));
    }

    public function store(StorePackageRequest $request, AuditService $auditService): RedirectResponse
    {
        $this->authorize('create', Package::class);

        $validated = $request->validated();
        $items = $validated['items'] ?? [];
        unset($validated['items']);

        $package = DB::transaction(function () use ($validated, $items): Package {
            $package = Package::query()->create($validated);
            $this->syncItems($package, $items);

            return $package->load('items.product');
        });

        $auditService->log('package.created', $package, null, $package->toArray());

        return redirect()
            ->route('packages.show', $package)
            ->with('success', 'Package created successfully.');
    }

    public function show(Package $package): View
    {
        $this->authorize('view', $package);

        $package->load(['items.product']);

        return view('packages.show', compact('package'));
    }

    public function edit(Package $package): View
    {
        $this->authorize('update', $package);

        $package->load('items.product');
        $products = Product::query()->where('is_active', true)->orderBy('name')->get();
        $packageTypes = PackageType::cases();

        return view('packages.edit', compact('package', 'products', 'packageTypes'));
    }

    public function update(UpdatePackageRequest $request, Package $package, AuditService $auditService): RedirectResponse
    {
        $this->authorize('update', $package);

        $validated = $request->validated();
        $items = $validated['items'] ?? null;
        unset($validated['items']);

        $oldValues = $package->toArray();

        DB::transaction(function () use ($package, $validated, $items): void {
            $package->update($validated);

            if ($items !== null) {
                $package->items()->delete();
                $this->syncItems($package, $items);
            }
        });

        $package->refresh()->load('items.product');

        $auditService->log('package.updated', $package, $oldValues, $package->toArray());

        return redirect()
            ->route('packages.show', $package)
            ->with('success', 'Package updated successfully.');
    }

    public function destroy(Package $package, AuditService $auditService): RedirectResponse
    {
        $this->authorize('delete', $package);

        $oldValues = $package->toArray();
        $package->delete();

        $auditService->log('package.archived', $package, $oldValues);

        return redirect()
            ->route('packages.index')
            ->with('success', 'Package archived successfully.');
    }

    public function duplicate(Package $package, PackageService $packageService, AuditService $auditService): RedirectResponse
    {
        $this->authorize('duplicate', $package);

        $duplicate = $packageService->duplicate($package);

        $auditService->log('package.duplicated', $duplicate, null, [
            'source_package_id' => $package->id,
            'new_package_id' => $duplicate->id,
        ]);

        return redirect()
            ->route('packages.edit', $duplicate)
            ->with('success', 'Package duplicated successfully.');
    }

    public function estimate(EstimatePackageRequest $request, Package $package, PackageService $packageService): JsonResponse
    {
        $this->authorize('estimate', $package);

        $validated = $request->validated();

        $estimate = $packageService->estimate(
            $package,
            (int) ($validated['guest_count'] ?? 1),
            (int) ($validated['billable_days'] ?? 1),
            (float) ($validated['billable_hours'] ?? 1),
            [
                'overall_discount_amount' => $validated['overall_discount_amount'] ?? 0,
                'overall_discount_percent' => $validated['overall_discount_percent'] ?? 0,
                'service_charge_rate' => $validated['service_charge_rate'] ?? 0,
                'service_charge_amount' => $validated['service_charge_amount'] ?? 0,
                'deposit_percent' => $validated['deposit_percent'] ?? 0,
                'amount_paid' => $validated['amount_paid'] ?? 0,
            ],
        );

        return response()->json($estimate);
    }

    /**
     * @param  list<array<string, mixed>>  $items
     */
    private function syncItems(Package $package, array $items): void
    {
        foreach ($items as $index => $item) {
            $package->items()->create([
                'product_id' => $item['product_id'],
                'description_override' => $item['description_override'] ?? null,
                'default_quantity' => $item['default_quantity'] ?? 1,
                'charge_basis_override' => $item['charge_basis_override'] ?? null,
                'price_override' => $item['price_override'] ?? null,
                'is_mandatory' => $item['is_mandatory'] ?? true,
                'is_optional' => $item['is_optional'] ?? false,
                'is_complimentary' => $item['is_complimentary'] ?? false,
                'sort_order' => $item['sort_order'] ?? $index,
            ]);
        }
    }
}
