<?php

namespace App\Http\Controllers\Care;

use App\Http\Controllers\Controller;
use App\Http\Controllers\Care\Concerns\ScopesToAccount;
use App\Models\CareUnit;
use App\Models\Department;
use App\Models\Member;
use App\Models\StaffAssignment;
use App\Models\User;
use App\Services\Care\AuditLogger;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\View\View;

class StaffAssignmentController extends Controller
{
    use ScopesToAccount;

    public function index(Request $request): View
    {
        $this->authorizeAbility($request, 'admin.members.view');
        $organization = $this->organization($request);
        $owner = $this->ownerRef($request);

        $assignments = StaffAssignment::owned($owner)
            ->where('organization_id', $organization->id)
            ->unitPlacements()
            ->with(['member.branch', 'department.branch', 'careUnit'])
            ->orderByDesc('starts_on')
            ->orderByDesc('id')
            ->get();

        $heroStats = [
            'total' => $assignments->count(),
            'active' => $assignments->where('status', 'active')->filter(fn (StaffAssignment $a) => $a->isEffectiveOn())->count(),
            'temporary' => $assignments->where('kind', 'temporary')->count(),
            'primary' => $assignments->where('kind', 'primary')->count(),
        ];

        return view('care.admin.staff-assignments.index', [
            'assignments' => $assignments,
            'organization' => $organization,
            'kinds' => config('care.staff_assignment_kinds'),
            'statuses' => config('care.staff_assignment_statuses'),
            'assignmentRoles' => config('care.staff_assignment_roles'),
            'memberLabels' => $this->memberLabels($assignments->pluck('member')->filter()),
            'heroStats' => $heroStats,
        ]);
    }

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

        return view('care.admin.staff-assignments.create', $this->formData($request));
    }

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

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

        $assignment = StaffAssignment::create([
            'owner_ref' => $owner,
            'organization_id' => $organization->id,
            ...$validated,
        ]);

        AuditLogger::record($owner, 'staff_assignment.created', $organization->id, $owner, StaffAssignment::class, $assignment->id);

        return redirect()->route('care.staff-assignments.index')->with('success', 'Unit assignment created.');
    }

    public function edit(Request $request, StaffAssignment $staffAssignment): View
    {
        $this->authorizeAbility($request, 'admin.members.manage');
        $this->authorizeOwner($request, $staffAssignment);

        return view('care.admin.staff-assignments.edit', [
            ...$this->formData($request),
            'assignment' => $staffAssignment,
        ]);
    }

    public function update(Request $request, StaffAssignment $staffAssignment): RedirectResponse
    {
        $this->authorizeAbility($request, 'admin.members.manage');
        $this->authorizeOwner($request, $staffAssignment);
        $organization = $this->organization($request);
        $owner = $this->ownerRef($request);

        $validated = $this->validated($request, $organization->id, $owner);
        $staffAssignment->update($validated);

        AuditLogger::record($owner, 'staff_assignment.updated', $organization->id, $owner, StaffAssignment::class, $staffAssignment->id);

        return redirect()->route('care.staff-assignments.index')->with('success', 'Unit assignment updated.');
    }

    public function destroy(Request $request, StaffAssignment $staffAssignment): RedirectResponse
    {
        $this->authorizeAbility($request, 'admin.members.manage');
        $this->authorizeOwner($request, $staffAssignment);

        $staffAssignment->delete();

        AuditLogger::record(
            $this->ownerRef($request),
            'staff_assignment.deleted',
            $staffAssignment->organization_id,
            $this->ownerRef($request),
            StaffAssignment::class,
            $staffAssignment->id,
        );

        return redirect()->route('care.staff-assignments.index')->with('success', 'Unit assignment removed.');
    }

    /**
     * @return array<string, mixed>
     */
    protected function formData(Request $request): array
    {
        $organization = $this->organization($request);
        $owner = $this->ownerRef($request);

        $members = Member::owned($owner)
            ->where('organization_id', $organization->id)
            ->with('branch')
            ->orderBy('role')
            ->get();

        $departments = Department::owned($owner)
            ->whereHas('branch', fn ($q) => $q->where('organization_id', $organization->id))
            ->with('branch')
            ->where('is_active', true)
            ->orderBy('name')
            ->get();

        $units = CareUnit::owned($owner)
            ->where('organization_id', $organization->id)
            ->where('is_active', true)
            ->with('department')
            ->orderBy('name')
            ->get();

        return [
            'organization' => $organization,
            'members' => $members,
            'memberLabels' => $this->memberLabels($members),
            'departments' => $departments,
            'units' => $units,
            'kinds' => config('care.staff_assignment_kinds'),
            'statuses' => config('care.staff_assignment_statuses'),
            'assignmentRoles' => config('care.staff_assignment_roles'),
            'rbacRoles' => config('care.roles'),
            'selectedMemberId' => $request->integer('member_id') ?: null,
        ];
    }

    /**
     * @return array<string, mixed>
     */
    protected function validated(Request $request, int $organizationId, string $owner): array
    {
        $validated = $request->validate([
            'member_id' => ['required', 'integer', 'exists:care_members,id'],
            'department_id' => ['nullable', 'integer', 'exists:care_departments,id'],
            'care_unit_id' => [
                'nullable',
                'integer',
                'exists:care_care_units,id',
                Rule::requiredIf(fn () => $request->input('kind') === 'temporary'),
            ],
            'kind' => ['required', 'string', Rule::in(array_keys(config('care.staff_assignment_kinds')))],
            'assignment_role' => ['nullable', 'string', Rule::in(array_keys(config('care.staff_assignment_roles')))],
            'starts_on' => ['required', 'date'],
            'ends_on' => ['nullable', 'date', 'after_or_equal:starts_on'],
            'status' => ['required', 'string', Rule::in(array_keys(config('care.staff_assignment_statuses')))],
            'notes' => ['nullable', 'string', 'max:2000'],
        ], [
            'care_unit_id.required' => 'Temporary unit assignments require a care unit.',
        ]);

        $member = Member::owned($owner)->findOrFail($validated['member_id']);
        abort_unless((int) $member->organization_id === $organizationId, 404);

        if (! empty($validated['department_id'])) {
            $department = Department::owned($owner)->with('branch')->findOrFail($validated['department_id']);
            abort_unless((int) $department->branch?->organization_id === $organizationId, 404);
        } else {
            $validated['department_id'] = null;
        }

        if (! empty($validated['care_unit_id'])) {
            $unit = CareUnit::owned($owner)->findOrFail($validated['care_unit_id']);
            abort_unless((int) $unit->organization_id === $organizationId, 404);
            if (! empty($validated['department_id']) && (int) $unit->department_id !== (int) $validated['department_id']) {
                throw \Illuminate\Validation\ValidationException::withMessages([
                    'care_unit_id' => 'Care unit must belong to the selected department.',
                ]);
            }
            if (empty($validated['department_id'])) {
                $validated['department_id'] = $unit->department_id;
            }
        } else {
            $validated['care_unit_id'] = null;
        }

        return [
            'member_id' => (int) $validated['member_id'],
            'department_id' => $validated['department_id'] ? (int) $validated['department_id'] : null,
            'care_unit_id' => $validated['care_unit_id'] ? (int) $validated['care_unit_id'] : null,
            'kind' => $validated['kind'],
            'assignment_role' => $validated['assignment_role'] ?? null,
            'shift_code' => null,
            'starts_on' => $validated['starts_on'],
            'ends_on' => $validated['ends_on'] ?? null,
            'status' => $validated['status'],
            'notes' => $validated['notes'] ?? null,
        ];
    }

    /**
     * @param  iterable<Member|null>  $members
     * @return array<int, string>
     */
    protected function memberLabels(iterable $members): array
    {
        $list = collect($members)->filter()->unique('id')->values();
        $refs = $list->pluck('user_ref')->all();
        $emails = User::query()->whereIn('public_id', $refs)->pluck('email', 'public_id');
        $roles = config('care.roles');

        $labels = [];
        foreach ($list as $member) {
            $display = str_contains((string) $member->user_ref, '@')
                ? $member->user_ref
                : ($emails[$member->user_ref] ?? $member->user_ref);
            $roleLabel = $roles[$member->role] ?? $member->role;
            $labels[(int) $member->id] = $display.' · '.$roleLabel;
        }

        return $labels;
    }
}
