<?php

namespace App\Http\Controllers\Web\Payroll;

use App\Http\Controllers\Controller;
use App\Models\EmployeePayroll;
use App\Models\PayrollCycle;
use App\Models\PayrollRunLog;
use App\Models\User;
use App\Notifications\PayslipReadyNotification;
use App\Services\AuditService;
use App\Services\Payroll\PayrollCycleService;
use App\Services\Payroll\PayrollRunService;
use Illuminate\Http\Request;

class PayrollRunController extends Controller
{
    public function __construct(
        protected PayrollRunService $runService,
        protected PayrollCycleService $cycleService,
        protected AuditService $audit
    ) {}

    protected function redirectToRunTab(PayrollCycle $cycle, ?string $flashKey = null, ?string $flashMessage = null)
    {
        $redirect = redirect()->route('payroll.index', [
            'tab' => 'run',
            'year' => $cycle->year,
            'month' => $cycle->month,
        ]);

        if ($flashKey && $flashMessage) {
            $redirect->with($flashKey, $flashMessage);
        }

        return $redirect;
    }

    public function run(Request $request, PayrollCycle $cycle)
    {
        $companyId = auth()->user()->company_id;
        if ($companyId && (int) $cycle->company_id !== $companyId) {
            abort(403);
        }
        try {
            $this->cycleService->ensureRunnable($cycle);
        } catch (\RuntimeException $e) {
            return $this->redirectToRunTab($cycle, 'error', $e->getMessage());
        }
        $result = $this->runService->run($cycle, auth()->id());
        $msg = $result['processed'] . ' employee(s) processed.';
        if ($result['skipped'] > 0) {
            $msg .= ' ' . $result['skipped'] . ' skipped.';
        }
        if (! empty($result['errors'])) {
            $msg .= ' Errors: ' . implode('; ', array_slice($result['errors'], 0, 3));
        }
        return $this->redirectToRunTab($cycle, 'success', $msg);
    }

    public function complete(Request $request, PayrollCycle $cycle)
    {
        $companyId = auth()->user()->company_id;
        if ($companyId && (int) $cycle->company_id !== $companyId) {
            abort(403);
        }
        try {
            $this->cycleService->ensureRunnable($cycle);
            $this->cycleService->transitionToCompleted($cycle, auth()->id());
            PayrollRunLog::withoutGlobalScopes()->create([
                'company_id' => $cycle->company_id,
                'payroll_cycle_id' => $cycle->id,
                'actor_user_id' => auth()->id(),
                'action' => 'completed',
                'payload' => ['at' => now()->toIso8601String()],
            ]);
            $this->audit->log('payroll.cycle.completed', $cycle);
            $this->notifyPayslipsReady($cycle);
        } catch (\RuntimeException $e) {
            return $this->redirectToRunTab($cycle, 'error', $e->getMessage());
        }
        return $this->redirectToRunTab($cycle, 'success', 'Payroll marked as completed.');
    }

    protected function notifyPayslipsReady(PayrollCycle $cycle): void
    {
        EmployeePayroll::withoutGlobalScopes()
            ->where('payroll_cycle_id', $cycle->id)
            ->where('company_id', $cycle->company_id)
            ->orderBy('id')
            ->chunkById(100, function ($payrolls) use ($cycle) {
                foreach ($payrolls as $employeePayroll) {
                    $user = User::query()->find($employeePayroll->user_id);
                    if (! $user) {
                        continue;
                    }
                    $user->notify(new PayslipReadyNotification($employeePayroll, $cycle));
                }
            });
    }

    public function lock(Request $request, PayrollCycle $cycle)
    {
        $companyId = auth()->user()->company_id;
        if ($companyId && (int) $cycle->company_id !== $companyId) {
            abort(403);
        }
        $this->cycleService->lock($cycle, auth()->id());
        PayrollRunLog::withoutGlobalScopes()->create([
            'company_id' => $cycle->company_id,
            'payroll_cycle_id' => $cycle->id,
            'actor_user_id' => auth()->id(),
            'action' => 'locked',
            'payload' => ['at' => now()->toIso8601String()],
        ]);
        $this->audit->log('payroll.cycle.locked', $cycle);
        return $this->redirectToRunTab($cycle, 'success', 'Payroll cycle locked.');
    }

    public function unlock(Request $request, PayrollCycle $cycle)
    {
        $companyId = auth()->user()->company_id;
        if ($companyId && (int) $cycle->company_id !== $companyId) {
            abort(403);
        }
        $this->cycleService->unlock($cycle);
        PayrollRunLog::withoutGlobalScopes()->create([
            'company_id' => $cycle->company_id,
            'payroll_cycle_id' => $cycle->id,
            'actor_user_id' => auth()->id(),
            'action' => 'unlocked',
            'payload' => ['at' => now()->toIso8601String()],
        ]);
        $this->audit->log('payroll.cycle.unlocked', $cycle);
        return $this->redirectToRunTab($cycle, 'success', 'Payroll cycle unlocked.');
    }
}
