<?php

namespace App\Modules\Payroll\Controllers;

use App\Http\Controllers\Controller;
use App\Modules\Admin\Tenancy\Models\Tenant;
use App\Modules\Payroll\Events\PayrollAdjustmentCreated;
use App\Modules\Payroll\Jobs\GeneratePayrollSnapshotsJob;
use App\Modules\Payroll\Models\PayrollPeriod;
use App\Modules\Payroll\Repositories\PayrollAdjustmentRepository;
use App\Modules\Payroll\Repositories\PayrollEmployeeSnapshotRepository;
use App\Modules\Payroll\Repositories\PayrollPeriodRepository;
use App\Modules\Payroll\Requests\StorePayrollAdjustmentRequest;
use App\Modules\Payroll\Requests\StorePayrollPeriodRequest;
use App\Modules\HR\Employees\Models\Employee;
use App\Modules\Payroll\Services\PayrollFinalizationService;
use App\Modules\Payroll\Services\PayrollProtectionService;
use App\Modules\Payroll\Support\PayrollPeriodStatus;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class PayrollPeriodsPageController extends Controller
{
    use AuthorizesRequests;

    public function __construct(
        private readonly PayrollPeriodRepository $periods,
        private readonly PayrollEmployeeSnapshotRepository $snapshots,
        private readonly PayrollAdjustmentRepository $adjustments,
        private readonly PayrollFinalizationService $finalization,
        private readonly PayrollProtectionService $payrollProtection,
    ) {}

    public function index(Request $request): View
    {
        $this->authorize('viewAny', PayrollPeriod::class);
        $tenant = $this->tenant();
        $paginator = $this->periods->paginateForIndex((int) $tenant->id, $request);

        return view('pages.payroll.periods.index', compact('paginator'));
    }

    public function create(): View
    {
        $this->authorize('create', PayrollPeriod::class);

        return view('pages.payroll.periods.create');
    }

    public function store(StorePayrollPeriodRequest $request): RedirectResponse
    {
        $this->authorize('create', PayrollPeriod::class);
        $tenant = $this->tenant();
        $this->periods->create([
            'tenant_id' => (int) $tenant->id,
            'name' => $request->validated('name'),
            'start_date' => $request->validated('start_date'),
            'end_date' => $request->validated('end_date'),
            'status' => PayrollPeriodStatus::DRAFT,
            'created_by' => (int) $request->user()->id,
        ]);

        return redirect()->route('payroll.periods.index')->with('status', __('payroll.flash.period_created'));
    }

    public function show(int $period): View
    {
        $tenant = $this->tenant();
        $row = $this->periods->findForTenant((int) $tenant->id, $period);
        abort_if($row === null, 404);
        $this->authorize('view', $row);
        $snapshots = $this->snapshots->paginateForPeriod((int) $tenant->id, (int) $row->id);
        $adjustmentPaginator = $this->adjustments->paginateForPeriod((int) $tenant->id, (int) $row->id);
        $employees = Employee::query()
            ->where('tenant_id', $tenant->id)
            ->where('is_active', true)
            ->orderBy('full_name')
            ->get(['id', 'full_name']);

        return view('pages.payroll.periods.show', [
            'period' => $row,
            'snapshots' => $snapshots,
            'adjustmentPaginator' => $adjustmentPaginator,
            'employees' => $employees,
        ]);
    }

    public function process(int $period): RedirectResponse
    {
        $tenant = $this->tenant();
        $row = $this->periods->findForTenant((int) $tenant->id, $period);
        abort_if($row === null, 404);
        $this->authorize('process', $row);
        $this->payrollProtection->assertPayrollReprocessAllowed($row, (int) request()->user()->id);
        GeneratePayrollSnapshotsJob::dispatch((int) $tenant->id, (int) $row->id);

        return redirect()
            ->route('payroll.periods.show', $row->id)
            ->with('status', __('payroll.flash.process_queued'));
    }

    public function finalize(int $period): RedirectResponse
    {
        $tenant = $this->tenant();
        $row = $this->periods->findForTenant((int) $tenant->id, $period);
        abort_if($row === null, 404);
        $this->authorize('finalize', $row);
        $this->finalization->finalize($row, request()->user());

        return redirect()
            ->route('payroll.periods.show', $row->id)
            ->with('status', __('payroll.flash.finalized'));
    }

    public function storeAdjustment(StorePayrollAdjustmentRequest $request, int $period): RedirectResponse
    {
        $tenant = $this->tenant();
        $row = $this->periods->findForTenant((int) $tenant->id, $period);
        abort_if($row === null, 404);
        $this->authorize('manageAdjustments', $row);
        $this->payrollProtection->assertPayrollEditable($row, (int) $request->user()->id);
        $v = $request->validated();
        $adj = $this->adjustments->create([
            'tenant_id' => (int) $tenant->id,
            'employee_id' => (int) $v['employee_id'],
            'payroll_period_id' => (int) $row->id,
            'type' => $v['type'],
            'amount' => $v['amount'],
            'reason' => $v['reason'],
            'created_by' => (int) $request->user()->id,
        ]);
        event(new PayrollAdjustmentCreated($adj));

        return redirect()
            ->route('payroll.periods.show', $row->id)
            ->with('status', __('payroll.flash.adjustment_added'));
    }

    private function tenant(): Tenant
    {
        $t = auth()->user()?->tenant;
        abort_if($t === null, 403);

        return $t;
    }
}
