<?php
namespace App\Http\Controllers\Company\Masters;

use App\Http\Controllers\Controller;
use App\Models\{
    AuditLog, Employee, EmployeeDocument, PayrollBranch, PayrollDepartment,
    PayrollDesignation, PayrollProject, PayrollSalaryComponent
};
use App\Services\EmployeeFormService;
use App\Support\EmployeeFormOptions;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;

class EmployeesController extends Controller
{
    public function index(Request $request)
    {
        $this->gate();
        $cid = auth()->user()->company_id;
        $q = Employee::withoutGlobalScopes()->where('company_id', $cid)->whereNull('deleted_at');
        if ($request->filled('search')) {
            $s = $request->search;
            $q->where(fn ($x) => $x->where('name', 'like', "%$s%")
                ->orWhere('display_name', 'like', "%$s%")
                ->orWhere('code', 'like', "%$s%")
                ->orWhere('email', 'like', "%$s%"));
        }
        if ($request->filled('status')) {
            $q->where('is_active', $request->status === 'active');
        }
        foreach (['department', 'designation', 'project', 'branch', 'blood_group'] as $field) {
            if ($request->filled($field)) {
                $q->where($field, $request->input($field));
            }
        }
        $employees = $q->orderBy('name')->paginate(25)->withQueryString();
        $stats = [
            'total'  => Employee::withoutGlobalScopes()->where('company_id', $cid)->whereNull('deleted_at')->count(),
            'active' => Employee::withoutGlobalScopes()->where('company_id', $cid)->whereNull('deleted_at')->where('is_active', true)->count(),
        ];

        $formEmployee = null;
        if ($request->get('open') !== 'create' && $request->filled('edit')) {
            $formEmployee = Employee::withoutGlobalScopes()
                ->where('company_id', $cid)
                ->whereNull('deleted_at')
                ->with(['salaryLines', 'documents'])
                ->find($request->edit);
        }

        return view('masters.employees.index', array_merge(
            compact('employees', 'stats', 'formEmployee'),
            $this->formContext($cid),
            ['bloodGroupOptions' => $this->bloodGroupOptions($cid)],
        ));
    }

    public function create()
    {
        $this->gate('create');
        return redirect()->route('masters.employees.index', ['open' => 'create']);
    }

    public function store(Request $request, EmployeeFormService $service)
    {
        $this->gate('create');
        $cid = auth()->user()->company_id;
        $validated = $request->validate($this->rules($cid));
        $attrs = $service->buildAttributes($validated, $request->boolean('is_inactive'));

        $emp = DB::transaction(function () use ($service, $attrs, $cid, $request) {
            $emp = Employee::withoutGlobalScopes()->create(array_merge($attrs, ['company_id' => $cid]));
            $service->syncSalaryLines($emp, $request);
            $service->storeDocuments($emp, $request);
            return $emp;
        });

        AuditLog::record('create', 'employees', (string) $emp->id, null, ['name' => $emp->name], "Employee created: {$emp->name}");

        return redirect()->route('masters.employees.index')->with('success', "Employee '{$emp->display_name}' created.");
    }

    public function edit(Employee $employee)
    {
        $this->gate('edit');
        $this->sc($employee);
        return redirect()->route('masters.employees.index', ['edit' => $employee->id]);
    }

    public function update(Request $request, Employee $employee, EmployeeFormService $service)
    {
        $this->gate('edit');
        $this->sc($employee);
        $cid = auth()->user()->company_id;
        $validated = $request->validate($this->rules($cid, $employee));
        $attrs = $service->buildAttributes($validated, $request->boolean('is_inactive'));

        DB::transaction(function () use ($service, $employee, $attrs, $request) {
            $employee->update($attrs);
            $service->syncSalaryLines($employee, $request);
            $service->storeDocuments($employee, $request);
        });

        AuditLog::record('update', 'employees', (string) $employee->id, null, ['name' => $employee->name], "Employee updated: {$employee->display_name}");

        return redirect()->route('masters.employees.index')->with('success', "Employee '{$employee->display_name}' updated.");
    }

    public function destroy(Employee $employee)
    {
        $this->gate('delete');
        $this->sc($employee);
        $guard = app(\App\Services\MasterDeletionGuard::class);
        if ($message = $guard->blockingMessage($employee)) {
            return back()->withErrors(['error' => $message]);
        }
        $n = $employee->name;
        $id = $employee->id;
        $guard->hardDelete($employee);
        AuditLog::record('hard_delete', 'employees', (string) $id, null, null, "Employee permanently deleted: {$n}");
        return back()->with('success', "Employee '$n' permanently deleted.");
    }

    public function toggleActive(Employee $employee)
    {
        $this->gate('edit');
        $this->sc($employee);
        $employee->update(['is_active' => !$employee->is_active]);
        return back()->with('success', 'Employee status updated.');
    }

    public function destroyDocument(Employee $employee, EmployeeDocument $document)
    {
        $this->gate('edit');
        $this->sc($employee);
        if ($document->employee_id !== $employee->id) {
            abort(404);
        }
        app(EmployeeFormService::class)->deleteDocument($document);
        return back()->with('success', 'Document removed.');
    }

    private function bloodGroupOptions(int $companyId): array
    {
        $fromEmployees = Employee::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->whereNull('deleted_at')
            ->whereNotNull('blood_group')
            ->where('blood_group', '!=', '')
            ->distinct()
            ->orderBy('blood_group')
            ->pluck('blood_group')
            ->all();

        $options = [];
        foreach (array_merge(EmployeeFormOptions::bloodGroups(), $fromEmployees) as $group) {
            $options[$group] = $group;
        }

        ksort($options);

        return $options;
    }

    private function formContext(int $companyId): array
    {
        $nameList = fn ($model) => $model::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->where('is_active', true)
            ->orderBy('name')
            ->pluck('name', 'name');

        $components = fn ($type) => PayrollSalaryComponent::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->where('type', $type)
            ->where('is_active', true)
            ->orderBy('name')
            ->get(['id', 'name']);

        return [
            'departmentOptions'   => $nameList(PayrollDepartment::class),
            'designationOptions'  => $nameList(PayrollDesignation::class),
            'projectOptions'      => $nameList(PayrollProject::class),
            'branchOptions'       => $nameList(PayrollBranch::class),
            'earningComponents'   => $components('earning'),
            'deductionComponents' => $components('deduction'),
            'irregularComponents' => $components('irregular'),
            'formOptions'         => EmployeeFormOptions::class,
        ];
    }

    private function rules(int $companyId, ?Employee $employee = null): array
    {
        $employeeId = $employee?->id;

        return [
            'display_name'          => 'required|string|max:150',
            'salutation'            => 'nullable|string|max:20',
            'first_name'            => 'nullable|string|max:80',
            'middle_name'           => 'nullable|string|max:80',
            'last_name'             => 'nullable|string|max:80',
            'father_name'           => 'nullable|string|max:120',
            'mother_name'           => 'nullable|string|max:120',
            'gender'                => 'nullable|string|max:20',
            'religion'              => 'nullable|string|max:50',
            'nationality'           => 'nullable|string|max:80',
            'spouse_name'           => 'nullable|string|max:120',
            'date_of_birth'         => 'nullable|date',
            'marital_status'        => 'nullable|string|max:30',
            'blood_group'           => 'nullable|string|max:10',
            'email'                 => 'nullable|email|max:150',
            'phone'                 => 'nullable|string|max:30',
            'mobile'                => 'nullable|string|max:30',
            'nid_no'                => 'nullable|string|max:30',
            'passport_no'           => 'nullable|string|max:30',
            'emergency_contact'     => 'nullable|string|max:120',
            'emergency_phone'       => 'nullable|string|max:30',
            'investment_amount'     => 'nullable|numeric|min:0',
            'present_address'       => 'nullable|string|max:2000',
            'permanent_address'     => 'nullable|string|max:2000',
            'reference_1'           => 'nullable|string|max:2000',
            'reference_2'           => 'nullable|string|max:2000',
            'is_inactive'           => 'nullable|boolean',
            'code'                  => ['nullable', 'string', 'max:30', Rule::unique('employees')->where(fn ($q) => $q->where('company_id', $companyId)->whereNull('deleted_at'))->ignore($employeeId)],
            'designation'           => 'nullable|string|max:100',
            'department'            => 'nullable|string|max:100',
            'project'               => 'nullable|string|max:120',
            'branch'                => 'nullable|string|max:120',
            'tax_id_tin'            => 'nullable|string|max:50',
            'joining_date'          => 'nullable|date',
            'release_date'          => 'nullable|date',
            'date_of_confirmation'  => 'nullable|date',
            'duty_type'             => 'nullable|string|max:50',
            'date_of_resign'        => 'nullable|date',
            'reason_for_resign'     => 'nullable|string|max:255',
            'terminated_employee'   => 'nullable|boolean',
            'payslip_send_method'   => 'nullable|string|max:50',
            'highest_education'     => 'nullable|string|max:120',
            'last_organization'     => 'nullable|string|max:150',
            'last_increment_date'   => 'nullable|date',
            'last_gross_salary'     => 'nullable|numeric|min:0',
            'salary_payment_method' => 'nullable|string|max:50',
            'bank_name'             => 'nullable|string|max:120',
            'branch_name'           => 'nullable|string|max:120',
            'account_no'            => 'nullable|string|max:50',
            'bank_account'          => 'nullable|string|max:50',
            'account_type'          => 'nullable|string|max:50',
            'employment_notes'      => 'nullable|string|max:5000',
            'parent_id'             => 'nullable|integer',
            'earnings'              => 'nullable|array',
            'earnings.*.salary_component_id' => 'nullable|integer',
            'earnings.*.component_name'      => 'nullable|string|max:120',
            'earnings.*.amount'              => 'nullable|numeric|min:0',
            'earnings.*.validity'            => 'nullable|string|max:50',
            'deductions'              => 'nullable|array',
            'deductions.*.salary_component_id' => 'nullable|integer',
            'deductions.*.component_name'      => 'nullable|string|max:120',
            'deductions.*.amount'              => 'nullable|numeric|min:0',
            'deductions.*.validity'            => 'nullable|string|max:50',
            'irregulars'              => 'nullable|array',
            'irregulars.*.salary_component_id' => 'nullable|integer',
            'irregulars.*.component_name'      => 'nullable|string|max:120',
            'irregulars.*.amount'              => 'nullable|numeric|min:0',
            'irregulars.*.qty_per_year'        => 'nullable|integer|min:0|max:365',
            'documents'             => 'nullable|array',
            'documents.*'           => 'file|max:10240',
        ];
    }

    private function gate(string $a = 'view')
    {
        if (!auth()->user()->hasPermission('masters', $a === 'view' ? 'view' : ($a === 'create' ? 'create' : ($a === 'edit' ? 'edit' : 'delete')))) {
            abort(403);
        }
    }

    private function sc(Employee $e)
    {
        if ($e->company_id !== auth()->user()->company_id) {
            abort(403);
        }
    }
}
