<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use App\Models\Asset;
use App\Models\AssetType;
use App\Models\User;
use App\Support\FormTextRules;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;

class AssetController extends Controller
{
    public function dashboard()
    {
        $companyId = Auth::user()?->company_id;
        $baseQuery = Asset::query();
        if ($companyId) {
            $baseQuery->where('company_id', $companyId);
        }

        $totalAssets = (clone $baseQuery)->count();

        $byStatus = (clone $baseQuery)->select('status', DB::raw('count(*) as total'))
            ->groupBy('status')
            ->pluck('total', 'status')
            ->toArray();

        $statusKeys = ['available', 'assigned', 'maintenance', 'retired'];
        foreach ($statusKeys as $key) {
            $byStatus[$key] = $byStatus[$key] ?? 0;
        }

        $byType = AssetType::withCount(['assets' => function ($q) use ($companyId) {
            if ($companyId) {
                $q->where('company_id', $companyId);
            }
        }])
            ->having('assets_count', '>', 0)
            ->orderByDesc('assets_count')
            ->get(['name', 'id'])
            ->map(fn ($t) => ['name' => $t->name, 'count' => $t->assets_count])
            ->values()
            ->toArray();

        $recentAssets = (clone $baseQuery)->with(['assetType', 'assignedUser'])
            ->orderByDesc('created_at')
            ->limit(5)
            ->get();

        $assignedPct = $totalAssets > 0
            ? round(($byStatus['assigned'] / $totalAssets) * 100)
            : 0;

        return view('assets.dashboard', compact(
            'totalAssets',
            'byStatus',
            'byType',
            'recentAssets',
            'assignedPct'
        ));
    }

    public function index(Request $request)
    {
        $companyId = Auth::user()?->company_id;
        $sort = $request->get('sort', 'name');
        $direction = strtolower((string) $request->get('direction', 'asc')) === 'desc' ? 'desc' : 'asc';

        $allowedSorts = [
            'name' => 'name',
            'serial' => 'serial_number',
            'status' => 'status',
        ];
        $orderColumn = $allowedSorts[$sort] ?? 'name';
        if (! isset($allowedSorts[$sort])) {
            $sort = 'name';
        }

        $query = Asset::with(['assetType', 'assignedUser'])->orderBy($orderColumn, $direction);
        if ($companyId) {
            $query->where('company_id', $companyId);
        }
        $assets = $query->paginate(15)->withQueryString();

        return view('assets.index', compact('assets', 'sort', 'direction'));
    }

    public function create()
    {
        $assetTypes = AssetType::orderBy('name')->get();
        $users = User::query()->forActiveDirectory();
        if (auth()->user()->company_id) {
            $users->where('company_id', auth()->user()->company_id);
        }
        $users = $users->orderBy('name')->get();

        return view('assets.create', compact('assetTypes', 'users'));
    }

    public function store(Request $request)
    {
        $companyId = auth()->user()->company_id;
        $request->merge([
            'serial_number' => $this->normalizeSerialNumber($request->input('serial_number')),
        ]);
        $validated = $request->validate([
            'name' => FormTextRules::notNumericOnly(true, 191),
            'asset_type_id' => 'required|exists:asset_types,id',
            'serial_number' => $this->serialNumberRules($companyId),
            'assigned_to' => 'required_if:status,assigned|nullable|exists:users,id',
            'status' => 'required|in:available,assigned,maintenance,retired',
            'purchase_date' => 'nullable|date|before_or_equal:today',
            'notes' => 'nullable|string|max:1000',
        ], array_merge(FormTextRules::messages(), [
            'serial_number.unique' => 'This serial number is already used for another asset.',
        ]));
        $validated['company_id'] = $companyId;
        if (! empty($validated['assigned_to'])) {
            $validated['status'] = 'assigned';
        }
        Asset::create($validated);

        return redirect()->route('assets.index')->with('success', 'Asset created successfully.');
    }

    public function edit(Asset $asset)
    {
        $this->authorizeAsset($asset);
        $assetTypes = AssetType::orderBy('name')->get();
        $users = User::query()->forActiveDirectory();
        if (auth()->user()->company_id) {
            $users->where('company_id', auth()->user()->company_id);
        }
        $users = $users->orderBy('name')->get();

        return view('assets.edit', compact('asset', 'assetTypes', 'users'));
    }

    public function update(Request $request, Asset $asset)
    {
        $this->authorizeAsset($asset);
        $companyId = auth()->user()->company_id;
        $request->merge([
            'serial_number' => $this->normalizeSerialNumber($request->input('serial_number')),
        ]);
        $validated = $request->validate([
            'name' => FormTextRules::notNumericOnly(true, 191),
            'asset_type_id' => 'required|exists:asset_types,id',
            'serial_number' => $this->serialNumberRules($companyId, (int) $asset->id),
            'assigned_to' => 'required_if:status,assigned|nullable|exists:users,id',
            'status' => 'required|in:available,assigned,maintenance,retired',
            'purchase_date' => 'nullable|date|before_or_equal:today',
            'notes' => 'nullable|string|max:1000',
        ], array_merge(FormTextRules::messages(), [
            'serial_number.unique' => 'This serial number is already used for another asset.',
        ]));

        if (! empty($validated['assigned_to'])) {
            $validated['status'] = $validated['status'] === 'retired' ? 'retired' : 'assigned';
        }

        $asset->update($validated);

        return redirect()->route('assets.index')->with('success', 'Asset updated successfully.');
    }

    public function destroy(Asset $asset)
    {
        $this->authorizeAsset($asset);
        $asset->delete();

        return redirect()->route('assets.index')->with('success', 'Asset removed successfully.');
    }

    public function returnAsset(Asset $asset)
    {
        $this->authorizeAsset($asset);
        $asset->update([
            'assigned_to' => null,
            'status' => 'available',
            'notes' => trim(($asset->notes ?? '') . "\nReturned on " . now()->format('Y-m-d')),
        ]);

        return redirect()->back()->with('success', 'Asset marked as returned.');
    }

    public function dispose(Asset $asset)
    {
        $this->authorizeAsset($asset);
        $asset->update([
            'assigned_to' => null,
            'status' => 'retired',
            'notes' => trim(($asset->notes ?? '') . "\nDisposed on " . now()->format('Y-m-d')),
        ]);

        return redirect()->back()->with('success', 'Asset disposed (retired).');
    }

    protected function authorizeAsset(Asset $asset): void
    {
        $companyId = Auth::user()?->company_id;
        if ($companyId && $asset->company_id !== $companyId) {
            abort(403);
        }
    }

    /**
     * @return list<mixed>
     */
    protected function serialNumberRules(?int $companyId, ?int $ignoreAssetId = null): array
    {
        $unique = Rule::unique('assets', 'serial_number')
            ->where(fn ($query) => $query->where('company_id', $companyId));

        if ($ignoreAssetId) {
            $unique->ignore($ignoreAssetId);
        }

        return array_merge(FormTextRules::alphanumericOptional(100), [$unique]);
    }

    protected function normalizeSerialNumber(?string $serialNumber): ?string
    {
        $serialNumber = trim((string) $serialNumber);

        return $serialNumber === '' ? null : $serialNumber;
    }
}
