<?php

namespace App\Services\Payroll;

use App\Models\PayrollCycle;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Collection;

class PayrollRunTabService
{
    public function __construct(
        protected PayrollBankExportService $bankExport
    ) {}

    /**
     * @param  Collection<int, \App\Models\EmployeePayroll>  $payrolls
     * @return array<string, mixed>
     */
    public function buildContext(
        int $companyId,
        PayrollCycle $cycle,
        Collection $payrolls,
        bool $isFutureMonth
    ): array {
        $eligibleCount = $this->eligibleEmployeeCount($companyId, $cycle, $isFutureMonth);
        $calculated = $payrolls->count();
        $missingBank = ($cycle->id && $calculated > 0)
            ? $this->bankExport->missingBankDetails($cycle)
            : [];

        return [
            'summary' => [
                'eligible' => $eligibleCount,
                'calculated' => $calculated,
                'pending_calculation' => max(0, $eligibleCount - $calculated),
                'gross_payroll' => (float) $payrolls->sum('gross_monthly'),
                'total_deductions' => (float) $payrolls->sum('total_deductions'),
                'net_payroll' => (float) $payrolls->sum('net_monthly'),
                'missing_bank_count' => count($missingBank),
            ],
            'workflow' => $this->workflowSteps($cycle, $isFutureMonth, $calculated),
            'readiness' => $this->readinessAlerts($cycle, $isFutureMonth, $calculated, $eligibleCount, $missingBank),
            'missing_bank_preview' => array_slice($missingBank, 0, 5),
            'currency' => (string) config('app.currency_symbol', '₹'),
            'period_label' => $this->periodLabel($cycle),
        ];
    }

    protected function periodLabel(PayrollCycle $cycle): string
    {
        $month = (int) ($cycle->month ?? 0);
        $year = (int) ($cycle->year ?? 0);
        if ($month < 1 || $year < 1) {
            return '—';
        }

        $start = $cycle->start_date?->format('j M');
        $end = $cycle->end_date?->format('j M Y');

        return trim(sprintf('%s %s', date('F', mktime(0, 0, 0, $month, 1)), $year))
            . ($start && $end ? " · {$start} – {$end}" : '');
    }

    protected function eligibleEmployeeCount(int $companyId, PayrollCycle $cycle, bool $isFutureMonth): int
    {
        if ($isFutureMonth || ! $cycle->id || ! $cycle->start_date || ! $cycle->end_date) {
            return 0;
        }

        $start = Carbon::parse($cycle->start_date);
        $end = Carbon::parse($cycle->end_date);

        return User::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->forPayrollPeriod($start, $end)
            ->count();
    }

    /**
     * @return list<array{key: string, label: string, state: string, hint: ?string}>
     */
    protected function workflowSteps(PayrollCycle $cycle, bool $isFutureMonth, int $calculated): array
    {
        if ($isFutureMonth) {
            return [
                ['key' => 'period', 'label' => 'Select period', 'state' => 'current', 'hint' => 'Future months cannot be processed'],
                ['key' => 'calculate', 'label' => 'Calculate', 'state' => 'upcoming', 'hint' => null],
                ['key' => 'review', 'label' => 'Review', 'state' => 'upcoming', 'hint' => null],
                ['key' => 'complete', 'label' => 'Complete', 'state' => 'upcoming', 'hint' => null],
                ['key' => 'lock', 'label' => 'Lock', 'state' => 'upcoming', 'hint' => null],
            ];
        }

        $status = (string) ($cycle->status ?? 'draft');
        $current = match (true) {
            $status === 'locked' => 'lock',
            in_array($status, ['completed'], true) => 'complete',
            $calculated > 0 && in_array($status, ['draft', 'processing'], true) => 'review',
            default => 'calculate',
        };

        $order = ['calculate', 'review', 'complete', 'lock'];
        $labels = [
            'calculate' => 'Calculate',
            'review' => 'Review',
            'complete' => 'Complete',
            'lock' => 'Lock',
        ];

        $steps = [];
        foreach ($order as $key) {
            $idx = array_search($key, $order, true);
            $curIdx = array_search($current, $order, true);
            $state = $idx < $curIdx ? 'done' : ($key === $current ? 'current' : 'upcoming');
            if ($status === 'locked' && $key === 'lock') {
                $state = 'done';
            }

            $steps[] = [
                'key' => $key,
                'label' => $labels[$key],
                'state' => $state,
                'hint' => match ($key) {
                    'calculate' => 'Run engine for all eligible employees',
                    'review' => 'Verify payslips, LOP, and deductions',
                    'complete' => 'Freeze numbers for payout',
                    'lock' => 'Prevent further edits',
                    default => null,
                },
            ];
        }

        return $steps;
    }

    /**
     * @param  list<string>  $missingBank
     * @return list<array{type: string, message: string}>
     */
    protected function readinessAlerts(
        PayrollCycle $cycle,
        bool $isFutureMonth,
        int $calculated,
        int $eligibleCount,
        array $missingBank
    ): array {
        $alerts = [];

        if ($isFutureMonth) {
            $alerts[] = [
                'type' => 'warning',
                'message' => 'This month has not started yet. Payroll can only be generated for the current month or earlier.',
            ];

            return $alerts;
        }

        if ($cycle->isLocked()) {
            $alerts[] = [
                'type' => 'info',
                'message' => 'This cycle is locked. Unlock only if you need to correct verified errors.',
            ];
        }

        if ($eligibleCount === 0) {
            $alerts[] = [
                'type' => 'warning',
                'message' => 'No eligible employees found for this period. Check joining dates, last working dates, and active status.',
            ];
        } elseif ($calculated === 0) {
            $alerts[] = [
                'type' => 'info',
                'message' => sprintf('%d eligible employee(s) — run Calculate payroll to generate payslips.', $eligibleCount),
            ];
        } elseif ($calculated < $eligibleCount) {
            $alerts[] = [
                'type' => 'warning',
                'message' => sprintf(
                    '%d of %d eligible employees calculated. Re-run calculate or review skipped profiles (missing salary / zero payable days).',
                    $calculated,
                    $eligibleCount
                ),
            ];
        }

        if ($missingBank !== []) {
            $alerts[] = [
                'type' => 'warning',
                'message' => sprintf(
                    '%d employee(s) missing bank account or IFSC — they will be excluded from NEFT export.',
                    count($missingBank)
                ),
            ];
        }

        return $alerts;
    }

    public function employeeStatusLabel(PayrollCycle $cycle, ?string $payrollStatus): string
    {
        if ($payrollStatus === 'paid') {
            return 'Paid';
        }

        if ($cycle->isLocked()) {
            return 'Locked';
        }

        if ($cycle->isCompleted()) {
            return 'Finalized';
        }

        return 'Calculated';
    }

    public function employeeStatusVariant(PayrollCycle $cycle, ?string $payrollStatus): string
    {
        if ($payrollStatus === 'paid') {
            return 'success';
        }

        if ($cycle->isLocked()) {
            return 'neutral';
        }

        if ($cycle->isCompleted()) {
            return 'info';
        }

        return 'warning';
    }
}
