<?php

namespace App\Http\Controllers;

use App\Http\Requests\StorePermanentEmployeeRequest;
use App\Http\Requests\UpdatePermanentEmployeeRequest;
use App\Http\Requests\UpdatePermanentEmployeeStatusRequest;
use App\Models\Designation;
use App\Models\PermanentEmployee;
use App\Models\PermanentEmployeeSalaryPayment;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Inertia\Inertia;
use Inertia\Response;

class PermanentEmployeeController extends Controller
{
    private const PER_PAGE_OPTIONS = [5, 10, 25, 50];

    private const STORAGE_PREFIX = 'permanent-employees';

    public function index(Request $request): Response
    {
        $perPage = $request->integer('per_page', 10);
        if (! in_array($perPage, self::PER_PAGE_OPTIONS, true)) {
            $perPage = 10;
        }

        $search = $request->string('search')->trim()->toString();

        $employees = PermanentEmployee::query()
            ->with('designation:id,name')
            ->with([
                'salaryPayments' => fn ($query) => $query
                    ->where('salary_increment', '>', 0)
                    ->orderByDesc('salary_month')
                    ->orderByDesc('id')
                    ->limit(1),
            ])
            ->withCount('punishments')
            ->when($search !== '', function ($query) use ($search): void {
                $query->where(function ($q) use ($search): void {
                    $q->where('full_name', 'like', '%'.$search.'%')
                        ->orWhere('father_name', 'like', '%'.$search.'%')
                        ->orWhere('national_card_number', 'like', '%'.$search.'%')
                        ->orWhere('phone_number', 'like', '%'.$search.'%')
                        ->orWhere('address', 'like', '%'.$search.'%')
                        ->orWhere('shift', 'like', '%'.$search.'%')
                        ->orWhereHas('designation', fn ($d) => $d->where('name', 'like', '%'.$search.'%'));
                });
            })
            ->orderByDesc('id')
            ->paginate($perPage)
            ->withQueryString()
            ->through(fn (PermanentEmployee $employee): array => $this->employeeListPayload($employee));

        return Inertia::render('employees/permanent/index', [
            'employees' => $employees,
            'filters' => [
                'search' => $search,
                'per_page' => $perPage,
            ],
        ]);
    }

    public function create(): Response
    {
        return Inertia::render('employees/permanent/create', [
            'designations' => Designation::optionsForForm(),
        ]);
    }

    public function store(StorePermanentEmployeeRequest $request): RedirectResponse
    {
        $validated = $request->validated();

        $employee = PermanentEmployee::query()->create([
            'designation_id' => $validated['designation_id'],
            'full_name' => $validated['full_name'],
            'father_name' => $validated['father_name'],
            'national_card_number' => $validated['national_card_number'],
            'phone_number' => $validated['phone_number'],
            'relative_phone_number' => $validated['relative_phone_number'] ?? null,
            'address' => $validated['address'],
            'monthly_salary' => $validated['monthly_salary'],
            'joining_date' => $validated['joining_date'],
            'shift' => $validated['shift'],
            'is_active' => $validated['is_active'],
        ]);

        $this->persistUploads($request, $employee);

        return redirect()
            ->route('employees.permanent.index')
            ->with('status', __('Permanent staff member registered.'));
    }

    public function edit(PermanentEmployee $permanentEmployee): Response
    {
        return Inertia::render('employees/permanent/edit', [
            'employee' => $this->employeeFormPayload($permanentEmployee),
            'designations' => Designation::optionsForForm($permanentEmployee->designation_id),
        ]);
    }

    public function update(UpdatePermanentEmployeeRequest $request, PermanentEmployee $permanentEmployee): RedirectResponse
    {
        $validated = $request->validated();

        $permanentEmployee->fill([
            'designation_id' => $validated['designation_id'],
            'full_name' => $validated['full_name'],
            'father_name' => $validated['father_name'],
            'national_card_number' => $validated['national_card_number'],
            'phone_number' => $validated['phone_number'],
            'relative_phone_number' => $validated['relative_phone_number'] ?? null,
            'address' => $validated['address'],
            'monthly_salary' => $validated['monthly_salary'],
            'joining_date' => $validated['joining_date'],
            'shift' => $validated['shift'],
            'is_active' => $validated['is_active'],
        ]);

        $permanentEmployee->save();

        PermanentEmployeeSalaryPayment::refreshDraftsForEmployee($permanentEmployee);

        $this->persistUploads($request, $permanentEmployee);

        return redirect()
            ->route('employees.permanent.index')
            ->with('status', __('Permanent staff member updated.'));
    }

    public function updateStatus(
        UpdatePermanentEmployeeStatusRequest $request,
        PermanentEmployee $permanentEmployee,
    ): RedirectResponse {
        $permanentEmployee->fill([
            'is_active' => $request->boolean('is_active'),
        ]);
        $permanentEmployee->save();

        return redirect()
            ->route('employees.permanent.index')
            ->with('status', __('Permanent staff status updated.'));
    }

    /**
     * @return array<string, mixed>
     */
    private function employeeListPayload(PermanentEmployee $employee): array
    {
        return [
            'id' => $employee->id,
            'designation_id' => $employee->designation_id,
            'designation_name' => $employee->designation?->name,
            'full_name' => $employee->full_name,
            'father_name' => $employee->father_name,
            'national_card_number' => $employee->national_card_number,
            'phone_number' => $employee->phone_number,
            'relative_phone_number' => $employee->relative_phone_number,
            'address' => $employee->address,
            'monthly_salary' => (string) $employee->monthly_salary,
            ...$this->latestSalaryIncrementPayload($employee),
            'punishments_count' => (int) ($employee->punishments_count ?? 0),
            'joining_date' => $employee->joining_date?->format('Y-m-d'),
            'shift' => $employee->shift,
            'is_active' => $employee->is_active,
            'image_url' => $employee->image_url,
            'national_card_url' => $employee->national_card_url,
            'health_card_url' => $employee->health_card_url,
            'labor_card_url' => $employee->labor_card_url,
            'contract_url' => $employee->contract_url,
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function employeeFormPayload(PermanentEmployee $employee): array
    {
        return [
            'id' => $employee->id,
            'designation_id' => $employee->designation_id,
            'designation_name' => $employee->designation?->name,
            'full_name' => $employee->full_name,
            'father_name' => $employee->father_name,
            'national_card_number' => $employee->national_card_number,
            'phone_number' => $employee->phone_number,
            'relative_phone_number' => $employee->relative_phone_number ?? '',
            'address' => $employee->address ?? '',
            'monthly_salary' => (string) $employee->monthly_salary,
            'joining_date' => $employee->joining_date?->format('Y-m-d'),
            'shift' => $employee->shift,
            'is_active' => $employee->is_active,
            'image_url' => $employee->image_url,
            'national_card_url' => $employee->national_card_url,
            'health_card_url' => $employee->health_card_url,
            'labor_card_url' => $employee->labor_card_url,
            'contract_url' => $employee->contract_url,
        ];
    }

    private function persistUploads(Request $request, PermanentEmployee $employee): void
    {
        $map = [
            'image' => 'image_path',
            'national_card' => 'national_card_path',
            'health_card' => 'health_card_path',
            'labor_card' => 'labor_card_path',
            'contract' => 'contract_path',
        ];

        $subdir = self::STORAGE_PREFIX.'/'.$employee->id;
        $dirty = false;

        foreach ($map as $input => $column) {
            if (! $request->hasFile($input)) {
                continue;
            }

            /** @var UploadedFile $file */
            $file = $request->file($input);
            $folder = match ($input) {
                'image' => $subdir.'/images',
                default => $subdir.'/documents',
            };

            if ($employee->{$column}) {
                $this->deletePublicFile($employee->{$column});
            }

            $employee->{$column} = $file->store($folder, 'public');
            $dirty = true;
        }

        if ($dirty) {
            $employee->save();
        }
    }

    /**
     * @return array{salary_before_increment: string|null, salary_after_increment: string|null, salary_increment_month: string|null}
     */
    private function latestSalaryIncrementPayload(PermanentEmployee $employee): array
    {
        $payment = $employee->relationLoaded('salaryPayments')
            ? $employee->salaryPayments->first()
            : $employee->salaryPayments()
                ->where('salary_increment', '>', 0)
                ->orderByDesc('salary_month')
                ->orderByDesc('id')
                ->first();

        if ($payment === null) {
            return [
                'salary_before_increment' => null,
                'salary_after_increment' => null,
                'salary_increment_month' => null,
            ];
        }

        $display = $payment->incrementSalaryDisplay();

        return [
            'salary_before_increment' => $display['salary_before_increment'] ?? null,
            'salary_after_increment' => $display['salary_after_increment'] ?? null,
            'salary_increment_month' => $payment->salary_month?->format('Y-m'),
        ];
    }

    private function deletePublicFile(string $path): void
    {
        /** @var FilesystemAdapter $disk */
        $disk = Storage::disk('public');

        if ($disk->exists($path)) {
            $disk->delete($path);
        }
    }
}
