<?php

namespace App\Http\Controllers;

use App\Models\Bank;
use App\Models\Nationality;
use App\Models\Site;
use App\Models\Staff;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rule;

class StaffController extends Controller
{
    public function index(Request $request)
    {
        $q        = trim((string) $request->get('q'));
        $status   = $request->get('status');
        $siteId   = $request->get('site_id');
        $natId    = $request->get('nationality_id');
        $expiring = $request->get('expiring'); // permit|passport|fomema

        $staff = Staff::query()
            ->with(['site', 'nationality'])
            ->when($q !== '', function ($query) use ($q) {
                // Trigram-backed partial search on indexed columns.
                $query->where(function ($w) use ($q) {
                    $w->where('name', 'ilike', "%{$q}%")
                      ->orWhere('passport_no', 'ilike', "%{$q}%")
                      ->orWhere('running_no', '=', ctype_digit($q) ? (int) $q : -1);
                });
            })
            ->when($status, fn ($query) => $query->where('status', $status))
            ->when($siteId, fn ($query) => $query->where('site_id', $siteId))
            ->when($natId, fn ($query) => $query->where('nationality_id', $natId))
            ->when($expiring, function ($query) use ($expiring) {
                $col = ['permit' => 'permit_expiry', 'passport' => 'passport_expiry', 'fomema' => 'fomema_expiry'][$expiring] ?? null;
                if ($col) {
                    $query->whereNotNull($col)->where($col, '<=', now()->addDays(90))->orderBy($col);
                }
            })
            ->orderBy('name')
            ->paginate(25)
            ->withQueryString();

        return view('staff.index', [
            'staff'         => $staff,
            'sites'         => Site::orderBy('name')->get(),
            'nationalities' => Nationality::orderBy('name')->get(),
            'filters'       => compact('q', 'status', 'siteId', 'natId', 'expiring'),
        ]);
    }

    public function show(Staff $staff)
    {
        $this->authorizeView($staff);
        $staff->load([
            'site', 'nationality', 'bank', 'renewals.creator', 'documents', 'payslips',
            'siteAssignments.site', 'siteAssignments.creator',
        ]);
        return view('staff.show', array_merge($this->formData(), ['staff' => $staff]));
    }

    // Serve a staff photo from private storage (auth-gated; staff see only their own).
    public function photo(Staff $staff)
    {
        $this->authorizeView($staff);
        abort_unless($staff->photo_path && Storage::disk('private')->exists($staff->photo_path), 404);
        return Storage::disk('private')->response($staff->photo_path);
    }

    // Reassign to a new site, keeping a dated assignment history.
    public function reassignSite(Request $request, Staff $staff)
    {
        $this->authorizeManage();
        $data = $request->validate([
            'site_id'     => ['required', 'exists:sites,id'],
            'assigned_on' => ['nullable', 'date'],
            'remarks'     => ['nullable', 'string'],
        ]);

        $assignedOn = $data['assigned_on'] ?? now()->toDateString();

        // Close the current open assignment.
        $staff->siteAssignments()->whereNull('ended_on')->update(['ended_on' => $assignedOn]);

        $staff->siteAssignments()->create([
            'site_id'     => $data['site_id'],
            'assigned_on' => $assignedOn,
            'remarks'     => $data['remarks'] ?? null,
            'created_by'  => auth()->id(),
        ]);

        $staff->update(['site_id' => $data['site_id']]);

        return back()->with('success', 'Site reassigned.');
    }

    public function create()
    {
        $this->authorizeManage();
        return view('staff.create', $this->formData());
    }

    public function store(Request $request)
    {
        $this->authorizeManage();
        $data = $this->validateData($request);
        $data['has_passport'] = $request->boolean('has_passport');
        $data['has_epass']    = $request->boolean('has_epass');
        $fileless = collect($data)->except(['photo', 'passport_file', 'permit_file'])->all();

        $staff = Staff::create($fileless);

        if ($request->hasFile('photo')) {
            $staff->update(['photo_path' => $request->file('photo')->store("staff/{$staff->id}/photo", 'private')]);
        }

        // Record the initial site assignment for history.
        if ($staff->site_id) {
            $staff->siteAssignments()->create([
                'site_id'     => $staff->site_id,
                'assigned_on' => $staff->date_of_join ?? now(),
                'created_by'  => auth()->id(),
            ]);
        }

        // Optional passport / permit scans uploaded during creation.
        $this->storeInitialDoc($request, $staff, 'passport_file', 'passport', 'Passport', $staff->passport_expiry);
        $this->storeInitialDoc($request, $staff, 'permit_file', 'permit', 'Work Permit', $staff->permit_expiry);

        return redirect()->route('staff.show', $staff)->with('success', 'Staff created.');
    }

    public function edit(Staff $staff)
    {
        $this->authorizeManage();
        return view('staff.edit', array_merge($this->formData(), ['staff' => $staff]));
    }

    public function update(Request $request, Staff $staff)
    {
        $this->authorizeManage();
        $data = $this->validateData($request, $staff->id);
        $data['has_passport'] = $request->boolean('has_passport');
        $data['has_epass']    = $request->boolean('has_epass');
        $data = collect($data)->except(['photo', 'passport_file', 'permit_file'])->all();

        if ($request->hasFile('photo')) {
            if ($staff->photo_path) {
                Storage::disk('private')->delete($staff->photo_path);
            }
            $data['photo_path'] = $request->file('photo')->store("staff/{$staff->id}/photo", 'private');
        }

        $staff->update($data);
        return redirect()->route('staff.show', $staff)->with('success', 'Profile updated.');
    }

    public function destroy(Staff $staff)
    {
        abort_unless(auth()->user()->isAdmin(), 403);
        $staff->delete();
        return redirect()->route('staff.index')->with('success', 'Staff removed.');
    }

    // ---- helpers ----

    private function formData(): array
    {
        return [
            'sites'         => Site::orderBy('name')->get(),
            'nationalities' => Nationality::orderBy('name')->get(),
            'banks'         => Bank::orderBy('name')->get(),
        ];
    }

    private function validateData(Request $request, ?int $id = null): array
    {
        return $request->validate([
            'running_no'      => ['nullable', 'integer'],
            'worker_code'     => ['nullable', 'string', 'max:50'],
            'site_ref'        => ['nullable', 'string', 'max:100'],
            'status'          => ['required', Rule::in(['active', 'resign', 'inactive'])],
            'name'            => ['required', 'string', 'max:255'],
            'passport_no'     => ['nullable', 'string', 'max:100', Rule::unique('staff', 'passport_no')->ignore($id)],
            'has_passport'    => ['nullable', 'boolean'],
            'has_epass'       => ['nullable', 'boolean'],
            'passport_expiry' => ['nullable', 'date'],
            'permit_expiry'   => ['nullable', 'date'],
            'fomema_expiry'   => ['nullable', 'date'],
            'site_id'         => ['nullable', 'exists:sites,id'],
            'nationality_id'  => ['nullable', 'exists:nationalities,id'],
            'basic_rate'      => ['nullable', 'numeric'],
            'pay_basis'       => ['nullable', Rule::in(['monthly', 'daily', 'hourly'])],
            'gender'          => ['nullable', Rule::in(['male', 'female'])],
            'bank_id'         => ['nullable', 'exists:banks,id'],
            'account_number'  => ['nullable', 'string', 'max:100'],
            'account_last4'   => ['nullable', 'string', 'max:10'],
            'designation'     => ['nullable', 'string', 'max:100'],
            'department'      => ['nullable', 'string', 'max:100'],
            'date_of_join'    => ['nullable', 'date'],
            'resign_date'     => ['nullable', 'date'],
            'photo'           => ['nullable', 'image', 'max:5120'],
            'passport_file'   => ['nullable', 'file', 'max:10240'],
            'permit_file'     => ['nullable', 'file', 'max:10240'],
        ]);
    }

    // Store an optional scan uploaded on the create form as a Document.
    private function storeInitialDoc(Request $request, Staff $staff, string $field, string $type, string $title, $expiry): void
    {
        if (! $request->hasFile($field)) {
            return;
        }
        $file = $request->file($field);
        $staff->documents()->create([
            'type'          => $type,
            'title'         => $title,
            'expiry_date'   => $expiry,
            'path'          => $file->store("documents/{$staff->id}", 'private'),
            'original_name' => $file->getClientOriginalName(),
            'mime'          => $file->getClientMimeType(),
            'uploaded_by'   => auth()->id(),
        ]);
    }

    private function authorizeManage(): void
    {
        abort_unless(auth()->user()->isAdmin() || auth()->user()->isSupervisor(), 403);
    }

    private function authorizeView(Staff $staff): void
    {
        $u = auth()->user();
        if ($u->isStaff() && $u->staff_id !== $staff->id) {
            abort(403);
        }
    }
}
