<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Resources\WarehouseAddressResource;
use App\Models\Warehouse;
use App\Models\WarehouseAddress;
use App\Support\CrmModuleManager;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;

class WarehouseAddressController extends Controller
{
    public function index(Warehouse $warehouse)
    {
        $this->authorize('view', $warehouse);

        $addresses = $warehouse->addresses()
            ->with('creator')
            ->latest('id')
            ->paginate(100)
            ->withQueryString();

        return WarehouseAddressResource::collection($addresses);
    }

    public function store(Request $request, Warehouse $warehouse, CrmModuleManager $moduleManager)
    {
        $this->authorize('update', $warehouse);

        $payload = $this->validatedData($request, $warehouse);
        $payload['warehouse_id'] = $warehouse->id;
        $payload['created_by'] = $request->user()->id;
        $payload = $moduleManager->applyPayloadHooks('warehouses.addresses.store', $payload, [
            'hook' => 'warehouses.addresses.store',
            'warehouse_id' => $warehouse->id,
            'user_id' => $request->user()->id,
            'source' => 'api',
        ], array_keys($payload));

        $address = WarehouseAddress::query()->create($payload);

        return WarehouseAddressResource::make($address->load('creator'))
            ->response()
            ->setStatusCode(201);
    }

    public function update(Request $request, Warehouse $warehouse, WarehouseAddress $warehouseAddress, CrmModuleManager $moduleManager): WarehouseAddressResource
    {
        $this->authorize('update', $warehouse);
        $this->ensureAddressBelongsToWarehouse($warehouse, $warehouseAddress);

        $payload = $this->validatedData($request, $warehouse, $warehouseAddress);
        $payload = $moduleManager->applyPayloadHooks('warehouses.addresses.update', $payload, [
            'hook' => 'warehouses.addresses.update',
            'warehouse_id' => $warehouse->id,
            'address_id' => $warehouseAddress->id,
            'user_id' => $request->user()->id,
            'source' => 'api',
        ], array_keys($payload));

        $warehouseAddress->update($payload);

        return WarehouseAddressResource::make($warehouseAddress->load('creator'));
    }

    public function destroy(Warehouse $warehouse, WarehouseAddress $warehouseAddress)
    {
        $this->authorize('update', $warehouse);
        $this->ensureAddressBelongsToWarehouse($warehouse, $warehouseAddress);

        $warehouseAddress->delete();

        return response()->noContent();
    }

    private function ensureAddressBelongsToWarehouse(Warehouse $warehouse, WarehouseAddress $warehouseAddress): void
    {
        abort_unless($warehouseAddress->warehouse_id === $warehouse->id, 404);
    }

    /**
     * @return array<string, mixed>
     */
    private function validatedData(Request $request, Warehouse $warehouse, ?WarehouseAddress $warehouseAddress = null): array
    {
        $validated = $request->validate([
            'code' => [
                'required',
                'string',
                'max:120',
                Rule::unique('warehouse_addresses', 'code')
                    ->where(fn ($query) => $query->where('warehouse_id', $warehouse->id))
                    ->ignore($warehouseAddress?->id),
            ],
            'zone' => ['nullable', 'string', 'max:80'],
            'aisle' => ['nullable', 'string', 'max:80'],
            'rack' => ['nullable', 'string', 'max:80'],
            'shelf' => ['nullable', 'string', 'max:80'],
            'cell' => ['nullable', 'string', 'max:80'],
            'x' => ['required', 'integer', 'min:1', 'max:200'],
            'y' => ['required', 'integer', 'min:1', 'max:200'],
            'color' => ['nullable', 'regex:/^#[0-9A-Fa-f]{6}$/'],
            'capacity' => ['nullable', 'numeric', 'min:0'],
            'current_load' => ['nullable', 'numeric', 'min:0'],
            'status' => ['required', Rule::in(['free', 'reserved', 'blocked'])],
            'note' => ['nullable', 'string'],
        ]);

        $maxRows = max(1, (int) $warehouse->map_rows);
        $maxColumns = max(1, (int) $warehouse->map_columns);
        if ((int) $validated['x'] > $maxColumns) {
            throw ValidationException::withMessages([
                'x' => __('X coordinate is outside the configured warehouse map.'),
            ]);
        }

        if ((int) $validated['y'] > $maxRows) {
            throw ValidationException::withMessages([
                'y' => __('Y coordinate is outside the configured warehouse map.'),
            ]);
        }

        $occupiedCoordinate = WarehouseAddress::query()
            ->where('warehouse_id', $warehouse->id)
            ->where('x', (int) $validated['x'])
            ->where('y', (int) $validated['y'])
            ->when($warehouseAddress !== null, fn ($query) => $query->where('id', '!=', $warehouseAddress->id))
            ->exists();
        if ($occupiedCoordinate) {
            throw ValidationException::withMessages([
                'x' => __('Selected map cell is already used by another storage address.'),
            ]);
        }

        $validated['zone'] = $this->nullableString($validated['zone'] ?? null);
        $validated['aisle'] = $this->nullableString($validated['aisle'] ?? null);
        $validated['rack'] = $this->nullableString($validated['rack'] ?? null);
        $validated['shelf'] = $this->nullableString($validated['shelf'] ?? null);
        $validated['cell'] = $this->nullableString($validated['cell'] ?? null);
        $validated['color'] = (string) ($validated['color'] ?? '#0EA5E9');
        $validated['capacity'] = (float) ($validated['capacity'] ?? 0);
        $validated['current_load'] = (float) ($validated['current_load'] ?? 0);
        $validated['note'] = $this->nullableString($validated['note'] ?? null);
        if ($validated['capacity'] > 0 && $validated['current_load'] > $validated['capacity']) {
            throw ValidationException::withMessages([
                'current_load' => __('Current load cannot be greater than capacity.'),
            ]);
        }

        return $validated;
    }

    private function nullableString(mixed $value): ?string
    {
        if (! is_string($value)) {
            return null;
        }

        $value = trim($value);

        return $value === '' ? null : $value;
    }
}
