<?php

namespace App\Console\Commands;

use App\Models\PayrollRun;
use App\Services\Payroll\PayrollCalculator;
use Carbon\Carbon;
use Illuminate\Console\Command;

class RunPayroll extends Command
{
    protected $signature = 'payroll:run
        {period : Pay period as YYYY-MM}
        {--ruleset=PITA : Statutory (PAYE) ruleset code}
        {--pay-date= : Payment date (YYYY-MM-DD), defaults to period end}
        {--force : Recalculate an existing DRAFT/CALCULATED run}';

    protected $description = 'Calculate a payroll run for a period, producing immutable payslips.';

    public function handle(PayrollCalculator $calculator): int
    {
        $period = $this->argument('period');
        if (! preg_match('/^\d{4}-\d{2}$/', $period)) {
            $this->error('Period must be in YYYY-MM format.');

            return self::FAILURE;
        }

        $start = Carbon::createFromFormat('Y-m-d', $period.'-01')->startOfMonth();
        $end = $start->copy()->endOfMonth();
        $payDate = $this->option('pay-date') ?: $end->toDateString();

        $run = PayrollRun::firstOrNew(['period' => $period]);

        if ($run->exists) {
            if ($run->isLocked()) {
                $this->error("Run {$period} is {$run->status} and cannot be recalculated. Use an adjustment run.");

                return self::FAILURE;
            }
            if (! $this->option('force')) {
                $this->error("Run {$period} already exists ({$run->status}). Pass --force to recalculate.");

                return self::FAILURE;
            }
            // Clear prior payslips in one statement (payslip_lines cascade at the
            // DB level). A chunked ->each(delete) would skip rows as pages shift.
            $run->payslips()->delete();
        }

        $run->fill([
            'period_start' => $start->toDateString(),
            'period_end' => $end->toDateString(),
            'pay_date' => $payDate,
            'effective_date' => $end->toDateString(),
            'statutory_ruleset' => strtoupper($this->option('ruleset')),
            'status' => PayrollRun::STATUS_DRAFT,
        ])->save();

        $this->info("Calculating payroll for {$period} (ruleset {$run->statutory_ruleset})...");
        $summary = $calculator->run($run->fresh());
        $run->refresh();

        $this->table(['Metric', 'Value'], [
            ['Employees', number_format($summary['employees'])],
            ['Total gross', number_format($run->total_gross, 2)],
            ['Total statutory', number_format($run->total_statutory, 2)],
            ['Total voluntary', number_format($run->total_voluntary, 2)],
            ['Total deductions', number_format($run->total_deductions, 2)],
            ['Total net pay', number_format($run->total_net, 2)],
            ['Unpriced (no salary row)', $summary['unpriced']],
            ['Status', $run->status],
        ]);

        if ($summary['unpriced'] > 0) {
            $this->warn("{$summary['unpriced']} employee(s) had no matching salary-table row (net excludes base pay).");
        }

        $this->info("Run #{$run->id} calculated.");

        return self::SUCCESS;
    }
}
