<?php

namespace App\Http\Controllers\Company\Payroll;

use App\Http\Controllers\Controller;
use App\Models\{
    AuditLog, Employee, PayrollBranch, PayrollDepartment, PayrollDesignation,
    PayrollProject, PayrollVoucher, Setting
};
use App\Services\PayrollSheetService;
use App\Support\{EmployeeFormOptions, PayrollVoucherOptions};
use Illuminate\Http\Request;

class PayrollSheetController extends Controller
{
    public function __construct(private PayrollSheetService $service) {}

    public function index(Request $request)
    {
        $this->ensurePayrollModule();
        $this->gate('view');

        $cid = auth()->user()->company_id;
        $generator = $this->service->normalizeGeneratorFilters($request->only([
            'voucher_type', 'payroll_year', 'payroll_month',
            'department', 'designation', 'project', 'branch', 'religion',
        ]));

        $matchCount = $this->service->employeesQuery($cid, $generator)->count();

        $listQuery = PayrollVoucher::withoutGlobalScopes()
            ->where('company_id', $cid);

        if ($request->filled('list_period')) {
            if (preg_match('/^([A-Za-z]{3})-(\d{4})$/', $request->list_period, $m)) {
                $month = array_search($m[1], PayrollVoucherOptions::months(), true);
                if ($month) {
                    $listQuery->where('payroll_year', (int) $m[2])->where('payroll_month', $month);
                }
            }
        }
        if ($request->filled('list_voucher_type')) {
            $listQuery->where('voucher_type', $request->list_voucher_type);
        }
        if ($request->filled('list_search')) {
            $search = $request->list_search;
            $listQuery->where(function ($q) use ($search) {
                $q->where('voucher_no', 'like', "%{$search}%")
                    ->orWhere('department', 'like', "%{$search}%")
                    ->orWhere('status', 'like', "%{$search}%");
            });
        }

        $vouchers = $listQuery->orderByDesc('payroll_year')
            ->orderByDesc('payroll_month')
            ->orderByDesc('id')
            ->paginate(20)
            ->withQueryString();

        return view('payroll.payroll-sheet.index', array_merge(
            compact('generator', 'matchCount', 'vouchers'),
            $this->filterOptions($cid),
        ));
    }

    public function print(Request $request, ?PayrollVoucher $payrollVoucher = null)
    {
        $this->ensurePayrollModule();
        $this->gate('view');

        $cid = auth()->user()->company_id;

        if ($payrollVoucher) {
            $this->sameCompany($payrollVoucher);
            $sheet = $this->service->sheetFromVoucher($payrollVoucher);
            $sheet['is_draft'] = false;
        } else {
            $filters = $this->service->normalizeGeneratorFilters($request->all());
            $sheet = $this->service->buildSheet($cid, $filters);
            $sheet['voucher_no'] = 'DRAFT';
            $sheet['is_draft'] = true;
        }

        $company = auth()->user()->company;
        $sheet['status_label'] = $this->service->filterStatusLabel($sheet);

        $view = ($sheet['layout'] ?? 'simple') === 'grouped_voucher_sheet'
            ? 'payroll.payroll-sheet.print-grouped-voucher-sheet'
            : 'payroll.payroll-sheet.print';

        return view($view, compact('sheet', 'company'));
    }

    public function submit(Request $request)
    {
        $this->ensurePayrollModule();
        $this->gate('edit');

        $cid = auth()->user()->company_id;
        $filters = $this->service->normalizeGeneratorFilters($request->all());

        if ($this->service->duplicateExists($cid, $filters)) {
            return back()
                ->withInput()
                ->withErrors(['voucher_type' => 'A voucher with these filters already exists for this period.']);
        }

        $voucher = $this->service->createVoucher($cid, $filters, auth()->id());

        AuditLog::record(
            'create',
            'payroll_vouchers',
            (string) $voucher->id,
            null,
            ['voucher_no' => $voucher->voucher_no],
            "Payroll voucher submitted: {$voucher->voucher_no}"
        );

        return redirect()
            ->route('payroll.payroll-sheet.index', $filters)
            ->with('success', "Payroll voucher {$voucher->voucher_no} submitted for approval.");
    }

    public function approve(PayrollVoucher $payrollVoucher)
    {
        $this->ensurePayrollModule();
        $this->gate('edit');
        $this->sameCompany($payrollVoucher);

        if ($payrollVoucher->status !== 'pending_approval') {
            return back()->withErrors(['approve' => 'Only pending vouchers can be approved.']);
        }

        $payrollVoucher->update([
            'status'      => 'approved',
            'approved_by' => auth()->id(),
            'approved_at' => now(),
        ]);

        return back()->with('success', "Voucher {$payrollVoucher->voucher_no} approved.");
    }

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

        $religions = Employee::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->whereNull('deleted_at')
            ->whereNotNull('religion')
            ->where('religion', '!=', '')
            ->distinct()
            ->orderBy('religion')
            ->pluck('religion', 'religion');

        if ($religions->isEmpty()) {
            $religions = collect(EmployeeFormOptions::religions());
        }

        return [
            'voucherTypes'  => PayrollVoucherOptions::types(),
            'years'         => PayrollVoucherOptions::years(),
            'months'        => PayrollVoucherOptions::months(),
            'departments'   => $lookup(PayrollDepartment::class),
            'designations'  => $lookup(PayrollDesignation::class),
            'projects'      => $lookup(PayrollProject::class),
            'branches'      => $lookup(PayrollBranch::class),
            'religions'     => $religions,
            'periodOptions' => $this->periodOptions($companyId),
        ];
    }

    private function periodOptions(int $companyId): array
    {
        return PayrollVoucher::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->select('payroll_year', 'payroll_month')
            ->distinct()
            ->orderByDesc('payroll_year')
            ->orderByDesc('payroll_month')
            ->get()
            ->mapWithKeys(fn ($row) => [
                PayrollVoucherOptions::periodLabel($row->payroll_year, $row->payroll_month)
                    => PayrollVoucherOptions::periodLabel($row->payroll_year, $row->payroll_month),
            ])
            ->all();
    }

    private function ensurePayrollModule(): void
    {
        if (Setting::get(auth()->user()->company_id, 'use_payroll_module', '0') !== '1') {
            abort(404);
        }
    }

    private function gate(string $action = 'view'): void
    {
        if (!auth()->user()->hasPermission('payroll', $action)) {
            abort(403);
        }
    }

    private function sameCompany(PayrollVoucher $voucher): void
    {
        if ($voucher->company_id !== auth()->user()->company_id) {
            abort(403);
        }
    }
}
