<?php

namespace App\Http\Controllers\Care;

use App\Http\Controllers\Controller;
use App\Http\Controllers\Care\Concerns\ScopesToAccount;
use App\Models\Branch;
use App\Models\Device;
use App\Services\Care\DeviceService;
use App\Services\Care\OrganizationResolver;
use App\Services\Care\PlanService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class DeviceController extends Controller
{
    use ScopesToAccount;

    public function __construct(
        protected DeviceService $devices,
        protected PlanService $plans,
    ) {}

    public function index(Request $request): View
    {
        $this->authorizeAbility($request, 'devices.view');
        $organization = $this->organization($request);
        $branchScope = app(OrganizationResolver::class)->branchScope($this->member($request));

        $query = Device::owned($this->ownerRef($request))
            ->where('organization_id', $organization->id)
            ->with('branch')
            ->when($branchScope, fn ($q) => $q->where('branch_id', $branchScope))
            ->orderBy('name');

        $devices = (clone $query)->paginate(25);

        $heroStats = [
            'total' => (clone $query)->count(),
            'online' => (clone $query)
                ->where('status', Device::STATUS_ACTIVE)
                ->where('last_seen_at', '>=', now()->subMinutes(10))
                ->count(),
            'agent' => (clone $query)->where('connection_mode', Device::MODE_AGENT)->count(),
        ];

        return view('care.devices.index', [
            'organization' => $organization,
            'devices' => $devices,
            'deviceTypes' => config('care.device_types'),
            'connectionModes' => config('care.device_connection_modes'),
            'heroStats' => $heroStats,
            'canManage' => app(\App\Services\Care\CarePermissions::class)
                ->can($this->member($request), 'devices.manage'),
            'hasAgentDevices' => $this->plans->hasPaidPlan($organization),
        ]);
    }

    public function create(Request $request): View
    {
        $this->authorizeAbility($request, 'devices.manage');
        $organization = $this->organization($request);

        return view('care.devices.create', [
            'organization' => $organization,
            'deviceTypes' => config('care.device_types'),
            'connectionModes' => config('care.device_connection_modes'),
            'branches' => $this->branches($request, $organization->id),
            'hasAgentDevices' => $this->plans->hasPaidPlan($organization),
            'agentTypes' => config('care.device_agent_types'),
            'defaultModes' => collect(config('care.device_types'))
                ->keys()
                ->mapWithKeys(fn ($type) => [$type => $this->devices->defaultConnectionMode($type)])
                ->all(),
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        $this->authorizeAbility($request, 'devices.manage');
        $organization = $this->organization($request);

        $validated = $this->validatedDevice($request, $organization);

        if (! $this->devices->canRegisterType($organization, $validated['type'], $this->plans)) {
            return back()->withInput()->with(
                'error',
                'Agent-connected clinical devices require Care Pro or Enterprise. USB barcode/QR scanners work on Free.',
            );
        }

        $mode = $validated['connection_mode']
            ?? $this->devices->defaultConnectionMode($validated['type']);

        $device = Device::create([
            'owner_ref' => $this->ownerRef($request),
            'organization_id' => $organization->id,
            'branch_id' => $validated['branch_id'] ?? null,
            'name' => $validated['name'],
            'type' => $validated['type'],
            'connection_mode' => $mode,
            'status' => Device::STATUS_OFFLINE,
            'metadata' => [],
        ]);

        $plainToken = null;
        if ($mode === Device::MODE_AGENT || in_array($validated['type'], config('care.device_agent_types', []), true)) {
            $plainToken = $this->devices->issueToken($device);
        }

        $redirect = redirect()->route('care.devices.edit', $device)
            ->with('success', 'Device registered.');

        if ($plainToken) {
            $redirect->with('device_token', $plainToken);
        }

        return $redirect;
    }

    public function edit(Request $request, Device $device): View
    {
        $this->authorizeAbility($request, 'devices.manage');
        $this->authorizeDevice($request, $device);

        return view('care.devices.edit', [
            'device' => $device,
            'organization' => $this->organization($request),
            'deviceTypes' => config('care.device_types'),
            'connectionModes' => config('care.device_connection_modes'),
            'deviceStatuses' => config('care.device_statuses'),
            'branches' => $this->branches($request, $device->organization_id),
            'hasAgentDevices' => $this->plans->hasPaidPlan($this->organization($request)),
            'agentTypes' => config('care.device_agent_types'),
            'connectionHint' => $this->devices->connectionHint($device->type, $device->connection_mode),
            'plainToken' => session('device_token'),
        ]);
    }

    public function update(Request $request, Device $device): RedirectResponse
    {
        $this->authorizeAbility($request, 'devices.manage');
        $this->authorizeDevice($request, $device);
        $organization = $this->organization($request);

        $validated = $this->validatedDevice($request, $organization, updating: true);

        if (
            isset($validated['type'])
            && $validated['type'] !== $device->type
            && ! $this->devices->canRegisterType($organization, $validated['type'], $this->plans)
        ) {
            return back()->withInput()->with(
                'error',
                'Agent-connected clinical devices require Care Pro or Enterprise.',
            );
        }

        $device->update([
            'name' => $validated['name'],
            'type' => $validated['type'],
            'branch_id' => $validated['branch_id'] ?? null,
            'connection_mode' => $validated['connection_mode']
                ?? $device->connection_mode
                ?? $this->devices->defaultConnectionMode($validated['type']),
            'status' => $validated['status'] ?? $device->status,
        ]);

        return redirect()->route('care.devices.index')->with('success', 'Device updated.');
    }

    public function destroy(Request $request, Device $device): RedirectResponse
    {
        $this->authorizeAbility($request, 'devices.manage');
        $this->authorizeDevice($request, $device);
        $device->delete();

        return redirect()->route('care.devices.index')->with('success', 'Device removed.');
    }

    public function regenerateToken(Request $request, Device $device): RedirectResponse
    {
        $this->authorizeAbility($request, 'devices.manage');
        $this->authorizeDevice($request, $device);

        $plain = $this->devices->issueToken($device);

        return back()
            ->with('success', 'Device token regenerated. Copy it now — it will not be shown again.')
            ->with('device_token', $plain);
    }

    public function revokeToken(Request $request, Device $device): RedirectResponse
    {
        $this->authorizeAbility($request, 'devices.manage');
        $this->authorizeDevice($request, $device);
        $this->devices->revokeToken($device);

        return back()->with('success', 'Device token revoked.');
    }

    protected function authorizeDevice(Request $request, Device $device): void
    {
        $this->authorizeOwner($request, $device);
        abort_unless($device->organization_id === $this->organization($request)->id, 404);

        $branchId = app(OrganizationResolver::class)->branchScope($this->member($request));
        if ($branchId !== null && $device->branch_id !== null && $device->branch_id !== $branchId) {
            abort(404);
        }
    }

    /**
     * @return array<string, mixed>
     */
    protected function validatedDevice(Request $request, $organization, bool $updating = false): array
    {
        $rules = [
            'name' => ['required', 'string', 'max:255'],
            'type' => ['required', 'string', 'in:'.implode(',', array_keys(config('care.device_types')))],
            'branch_id' => ['nullable', 'integer', 'exists:care_branches,id'],
            'connection_mode' => ['nullable', 'string', 'in:'.implode(',', array_keys(config('care.device_connection_modes')))],
        ];

        if ($updating) {
            $rules['status'] = ['nullable', 'string', 'in:'.implode(',', array_keys(config('care.device_statuses')))];
        }

        $validated = $request->validate($rules);

        if (! empty($validated['branch_id'])) {
            $ok = Branch::owned($this->ownerRef($request))
                ->where('organization_id', $organization->id)
                ->where('id', $validated['branch_id'])
                ->exists();
            abort_unless($ok, 422);
        }

        return $validated;
    }

    /** @return \Illuminate\Database\Eloquent\Collection<int, Branch> */
    protected function branches(Request $request, int $organizationId)
    {
        $branchScope = app(OrganizationResolver::class)->branchScope($this->member($request));

        return Branch::owned($this->ownerRef($request))
            ->where('organization_id', $organizationId)
            ->where('is_active', true)
            ->when($branchScope, fn ($q) => $q->where('id', $branchScope))
            ->orderBy('name')
            ->get();
    }
}
