<?php

namespace App\Http\Controllers\Web\Payroll;

use App\Http\Controllers\Controller;
use App\Models\CompanySetting;
use App\Models\Department;
use App\Models\EmployeePayroll;
use App\Models\Payroll;
use App\Models\PayrollPtSlab;
use App\Models\PayrollRunLog;
use App\Models\SalaryRevision;
use App\Services\Payroll\IncomeTaxDeclarationService;
use App\Services\Payroll\V2\PayrollCycleService;
use App\Support\FinancialYear;
use Carbon\Carbon;
use Illuminate\Http\Request;

class PayrollHomeController extends Controller
{
    public function __construct(
        protected PayrollCycleService $cycleService,
        protected IncomeTaxDeclarationService $incomeTaxService
    ) {}

    public function index(Request $request)
    {
        $user = $request->user();
        $companyId = $user?->company_id;
        if (! $companyId) {
            return redirect()->route('dashboard')->with('error', 'Company not found.');
        }

        $isAdmin = (bool) ($user->isTenantManager() ?? false);
        $tab = (string) ($request->query('tab') ?? '');
        $sub = (string) ($request->query('sub') ?? 'payroll_settings');

        $employeeSelfTabs = ['summary', 'my'];
        $adminTabs = ['run', 'setup', 'advanced', 'statutory', 'declaration', 'audit', 'payslips', ...$employeeSelfTabs];

        if ($isAdmin) {
            if ($tab !== '' && ! in_array($tab, $adminTabs, true)) {
                $tab = 'run';
            }
        } else {
            $tab = in_array($tab, $employeeSelfTabs, true) ? $tab : 'summary';
        }
        if ($tab === '') {
            $tab = $isAdmin ? 'run' : 'summary';
        }

        $data = [
            'tab' => $tab,
            'isAdmin' => $isAdmin,
            'sub' => $sub,
            'mySub' => 'salary',
            'showAdvanced' => in_array($tab, ['setup', 'advanced', 'statutory'], true),
        ];

        if ($isAdmin && $tab === 'run') {
            $year = $request->filled('year') ? (int) $request->year : (int) date('Y');
            $month = $request->filled('month') ? (int) $request->month : (int) date('n');
            $year = max(2020, min(2030, $year));
            $month = max(1, min(12, $month));

            // Reuse legacy dashboard data shape so UI stays identical
            $monthStatuses = $this->getLegacyMonthStatuses((int) $companyId, $year);

            $baseQuery = Payroll::with(['user.department', 'payrollComponents'])
                ->where('company_id', $companyId)
                ->where('year', $year)
                ->where('month', $month);

            if ($request->filled('search')) {
                $term = $request->search;
                $baseQuery->whereHas('user', function ($q) use ($term) {
                    $q->where('name', 'like', '%' . $term . '%')
                        ->orWhere('email', 'like', '%' . $term . '%');
                });
            }
            if ($request->filled('department')) {
                $baseQuery->whereHas('user', fn ($q) => $q->where('department_id', $request->department));
            }
            if ($request->filled('status') && in_array($request->status, ['paid', 'pending'], true)) {
                if ($request->status === 'paid') {
                    $baseQuery->where('status', 'paid');
                } else {
                    $baseQuery->where(function ($q) {
                        $q->whereNull('status')->orWhere('status', '!=', 'paid');
                    });
                }
            }

            $payrolls = $baseQuery->orderBy('user_id')->paginate(20)->withQueryString();

            $allForMonth = Payroll::withoutGlobalScopes()
                ->where('company_id', $companyId)
                ->where('year', $year)
                ->where('month', $month)
                ->get();

            $summary = [
                'total_employees' => $allForMonth->count(),
                'processed' => $allForMonth->where('status', 'paid')->count(),
                'pending' => $allForMonth->filter(fn ($p) => ($p->status ?? '') !== 'paid')->count(),
                'total_payroll' => $allForMonth->sum('net_salary'),
            ];

            $departments = Department::where('company_id', $companyId)->orderBy('name')->get();
            $filters = $request->only('search', 'department', 'status');

            $data = array_merge($data, compact(
                'payrolls',
                'year',
                'month',
                'monthStatuses',
                'summary',
                'departments',
                'filters'
            ));
        }

        if ($isAdmin && in_array($tab, ['setup', 'advanced', 'statutory'], true)) {
            $data['settings'] = [
                // Payroll policy
                'payroll_currency' => $this->getSetting((int) $companyId, 'payroll_currency', 'INR'),
                'payroll_pay_day' => $this->getSetting((int) $companyId, 'payroll_pay_day', 'last_day'),
                'payroll_rounding_mode' => $this->getSetting((int) $companyId, 'payroll_rounding_mode', 'nearest_rupee'),
                'payroll_lop_basis' => $this->getSetting((int) $companyId, 'payroll_lop_basis', 'gross'),
                'payroll_working_days_source' => $this->getSetting((int) $companyId, 'payroll_working_days_source', 'weekday_minus_holidays'),
                // Declaration
                'payroll_declaration_window_enabled' => $this->getSetting((int) $companyId, 'payroll_declaration_window_enabled', '0'),
                'declaration_from_day' => $this->getSetting((int) $companyId, 'declaration_from_day', '1'),
                'declaration_to_day' => $this->getSetting((int) $companyId, 'declaration_to_day', '25'),
                'declaration_year_from' => $this->getSetting((int) $companyId, 'declaration_year_from', ''),
                'declaration_year_to' => $this->getSetting((int) $companyId, 'declaration_year_to', ''),
                'proof_mandatory' => $this->getSetting((int) $companyId, 'proof_mandatory', '0'),
                'payroll_tds_enabled' => $this->getSetting((int) $companyId, 'payroll_tds_enabled', '0'),
                'income_tax_plan_old_id' => $this->getSetting((int) $companyId, 'income_tax_plan_old_id', ''),
                'income_tax_plan_new_id' => $this->getSetting((int) $companyId, 'income_tax_plan_new_id', ''),
                // Payout
                'payout_mode' => $this->getSetting((int) $companyId, 'payout_mode', 'bank_transfer'),
                'payout_requires_approval' => $this->getSetting((int) $companyId, 'payout_requires_approval', '0'),
                // Payslip
                'show_employer_contrib' => $this->getSetting((int) $companyId, 'show_employer_contrib', '0'),
                'show_ytd_totals' => $this->getSetting((int) $companyId, 'show_ytd_totals', '0'),
                'show_leave_balance' => $this->getSetting((int) $companyId, 'show_leave_balance', '0'),
                // Statutory (existing)
                'payroll_pf_enabled' => $this->getSetting((int) $companyId, 'payroll_pf_enabled', '0'),
                'pf_excluded_in_ctc' => $this->getSetting((int) $companyId, 'pf_excluded_in_ctc', '0'),
                'payroll_pf_rate' => $this->getSetting((int) $companyId, 'payroll_pf_rate', '12'),
                'payroll_pf_cap' => $this->getSetting((int) $companyId, 'payroll_pf_cap', '15000'),
                'payroll_esi_enabled' => $this->getSetting((int) $companyId, 'payroll_esi_enabled', '0'),
                'esi_excluded_in_ctc' => $this->getSetting((int) $companyId, 'esi_excluded_in_ctc', '0'),
                'payroll_esi_rate' => $this->getSetting((int) $companyId, 'payroll_esi_rate', '0.75'),
                'payroll_esi_cap' => $this->getSetting((int) $companyId, 'payroll_esi_cap', '21000'),
                'payroll_pt_enabled' => $this->getSetting((int) $companyId, 'payroll_pt_enabled', '0'),
                'payroll_pt_amount' => $this->getSetting((int) $companyId, 'payroll_pt_amount', '200'),
            ];

            if ($sub === 'pt') {
                $data['ptSlabs'] = PayrollPtSlab::withoutGlobalScopes()
                    ->where('company_id', $companyId)
                    ->orderBy('state')
                    ->orderBy('min_gross')
                    ->get();
            }

            if ($sub === 'declaration_settings') {
                $data['incomeTaxPlans'] = \App\Models\IncomeTaxPlan::query()
                    ->where('is_active', true)
                    ->orderBy('plan_name')
                    ->get();
            }
        }

        if ($isAdmin && $tab === 'declaration') {
            $fy = $request->query('fy', FinancialYear::forDate());
            $query = \App\Models\IncomeTaxDeclaration::withoutGlobalScopes()
                ->where('company_id', $companyId)
                ->with(['user', 'items'])
                ->where('financial_year', $fy)
                ->orderByDesc('submitted_at')
                ->orderByDesc('id');

            if ($request->filled('status')) {
                $query->where('status', $request->status);
            }

            $data['taxDeclarations'] = $query->get();
            $data['taxFinancialYear'] = $fy;
        }

        if ($isAdmin && $tab === 'payslips') {
            $query = Payroll::with('user')->where('company_id', $companyId);
            if ($request->filled('year')) {
                $query->where('year', $request->year);
            }
            if ($request->filled('month')) {
                $query->where('month', $request->month);
            }
            $data['legacyPayslips'] = $query->orderByDesc('year')->orderByDesc('month')->paginate(20)->withQueryString();
        }

        if ($isAdmin && $tab === 'audit') {
            $query = PayrollRunLog::withoutGlobalScopes()
                ->where('company_id', $companyId)
                ->with(['payrollCycle', 'actor'])
                ->orderByDesc('created_at');
            if ($request->filled('search')) {
                $term = $request->search;
                $query->where(function ($q) use ($term) {
                    $q->where('action', 'like', '%' . $term . '%')
                        ->orWhereHas('actor', fn ($a) => $a->where('name', 'like', '%' . $term . '%'));
                });
            }
            $data['auditLogs'] = $query->paginate(20)->withQueryString();
        }

        if ($tab === 'my') {
            $allowedMySubTabs = ['salary', 'payslips', 'income-tax'];
            $mySub = (string) $request->query('my_sub', 'salary');
            if (! in_array($mySub, $allowedMySubTabs, true)) {
                $mySub = 'salary';
            }
            $data['mySub'] = $mySub;

            $myPayrollsV2Query = EmployeePayroll::withoutGlobalScopes()
                ->where('company_id', $companyId)
                ->where('user_id', (int) $user->id)
                ->with('payrollCycle')
                ->orderByDesc('id');

            $latestV2Payroll = (clone $myPayrollsV2Query)->first();
            $latestLegacyPayroll = Payroll::withoutGlobalScopes()
                ->where('company_id', $companyId)
                ->where('user_id', (int) $user->id)
                ->orderByDesc('year')
                ->orderByDesc('month')
                ->first();

            $data['mySalarySummary'] = [
                'current_compensation_annual' => (float) ($user->latestEffectiveSalaryRevision()?->ctc_annual ?? 0),
                'pay_cycle' => 'Monthly',
            ];

            $salaryRevisions = SalaryRevision::withoutGlobalScopes()
                ->where('company_id', $companyId)
                ->where('user_id', (int) $user->id)
                ->orderByDesc('effective_date')
                ->orderByDesc('id')
                ->get();

            $salaryTimelineRows = $salaryRevisions->map(function (SalaryRevision $revision) {

                $components = collect(is_array($revision->components_json) ? $revision->components_json : []);

                $regularAnnual = (float) $components
                    ->where('type', 'earnings')
                    ->sum('annual');

                $bonusAnnual = (float) $components
                    ->filter(fn ($row) =>
                        is_array($row) && stripos((string) ($row['name'] ?? ''), 'bonus') !== false
                    )
                    ->sum(fn ($row) => (float) ($row['annual'] ?? 0));

                $otherAnnual =
                    (float) (($revision->pf_employer ?? 0) * 12) +
                    (float) (($revision->esi_employer ?? 0) * 12);

                $regularAnnual = round((float) $revision->ctc_annual - $otherAnnual - $bonusAnnual);

                return [
                    'effective_date' => $revision->effective_date?->format('d M Y'),
                    'regular' => round($regularAnnual),
                    'other' => round($otherAnnual),
                    'bonus' => round($bonusAnnual),
                    'total' => round((float) $revision->ctc_annual),
                    'view_url' => route('employees.salary-breakup', $revision->id),
                ];
            });

            if ($salaryTimelineRows->isEmpty()) {
                $salaryTimelineRows = $this->buildSalaryTimelineFallbackFromPayroll((int) $companyId, (int) $user->id);
            }
            $data['mySalaryTimelineRows'] = $salaryTimelineRows;

            if ($mySub === 'payslips') {
                $myPayrollsV2 = (clone $myPayrollsV2Query)->get();
                $useV2 = $myPayrollsV2->isNotEmpty();

                if ($useV2) {
                    $rows = $myPayrollsV2->map(function (EmployeePayroll $ep) {
                        $cycle = $ep->payrollCycle;
                        $month = (int) ($cycle?->month ?? 0);
                        $year = (int) ($cycle?->year ?? 0);
                        $gross = (float) ($ep->gross_monthly ?? 0);
                        $net = (float) ($ep->net_monthly ?? 0);

                        return [
                            'id' => (int) $ep->id,
                            'source' => 'v2',
                            'year' => $year,
                            'month' => $month,
                            'period' => ($month > 0 && $year > 0) ? (date('M', mktime(0, 0, 0, $month, 1)) . ' ' . $year) : '—',
                            'gross' => $gross,
                            'deductions' => max(0, $gross - $net),
                            'net' => $net,
                            'view_url' => route('payroll.my.payslips.show', $ep),
                            'pdf_url' => route('payroll.my.payslips.download', $ep),
                        ];
                    })->values();
                } else {
                    $legacyPayrolls = Payroll::withoutGlobalScopes()
                        ->where('company_id', $companyId)
                        ->where('user_id', (int) $user->id)
                        ->orderByDesc('year')
                        ->orderByDesc('month')
                        ->get();

                    $rows = $legacyPayrolls->map(function (Payroll $p) {
                        $month = (int) ($p->month ?? 0);
                        $year = (int) ($p->year ?? 0);
                        $gross = (float) ($p->salary_amount ?? 0);
                        $net = (float) ($p->net_salary ?? 0);

                        return [
                            'id' => (int) $p->id,
                            'source' => 'legacy',
                            'year' => $year,
                            'month' => $month,
                            'period' => ($month > 0 && $year > 0) ? (date('M', mktime(0, 0, 0, $month, 1)) . ' ' . $year) : '—',
                            'gross' => $gross,
                            'deductions' => max(0, $gross - $net),
                            'net' => $net,
                            'view_url' => route('payroll.my.legacy-payslips.show', $p),
                            'pdf_url' => route('payroll.my.legacy-payslips.download', $p),
                        ];
                    })->values();
                }

                $availableYears = $rows
                    ->pluck('year')
                    ->filter(fn ($year) => (int) $year > 0)
                    ->map(fn ($year) => (int) $year)
                    ->unique()
                    ->sortDesc()
                    ->values();

                $selectedYear = (int) $request->query('year', (int) ($availableYears->first() ?? date('Y')));
                if (! $availableYears->contains($selectedYear) && $availableYears->isNotEmpty()) {
                    $selectedYear = (int) $availableYears->first();
                }

                $rowsForYear = $rows
                    ->when($availableYears->isNotEmpty(), fn ($collection) => $collection->where('year', $selectedYear))
                    ->values();

                $selectedSource = (string) $request->query('source', '');
                $selectedId = (int) $request->query('payslip_id', 0);

                $selectedRow = $rowsForYear->first(function (array $row) use ($selectedSource, $selectedId) {
                    if ($selectedId <= 0) {
                        return false;
                    }
                    if ($selectedSource !== '' && $selectedSource !== ($row['source'] ?? '')) {
                        return false;
                    }

                    return (int) ($row['id'] ?? 0) === $selectedId;
                });
                $selectedRow = $selectedRow ?: $rowsForYear->first();

                $data['myPayslipRows'] = $rowsForYear;
                $data['myPayslipYears'] = $availableYears;
                $data['myPayslipSelectedYear'] = $selectedYear;
                $data['myPayslipSelected'] = $selectedRow;
                $data['myPayslipPreview'] = null;

                if ($selectedRow) {
                    if (($selectedRow['source'] ?? '') === 'v2') {
                        $selectedPayroll = EmployeePayroll::withoutGlobalScopes()
                            ->where('company_id', $companyId)
                            ->where('user_id', (int) $user->id)
                            ->with(['user.company', 'user.department', 'user.designation', 'user.location', 'payrollCycle', 'components'])
                            ->find((int) $selectedRow['id']);

                        if ($selectedPayroll) {
                            $data['myPayslipPreview'] = $this->buildMyPayslipPreviewFromV2($selectedPayroll);
                        }
                    } else {
                        $selectedPayroll = Payroll::withoutGlobalScopes()
                            ->where('company_id', $companyId)
                            ->where('user_id', (int) $user->id)
                            ->with(['user.company', 'user.department', 'user.designation', 'user.location', 'payrollComponents'])
                            ->find((int) $selectedRow['id']);

                        if ($selectedPayroll) {
                            $data['myPayslipPreview'] = $this->buildMyPayslipPreviewFromLegacy($selectedPayroll);
                        }
                    }
                }
            }

            if ($mySub === 'income-tax') {
                $fy = $request->query('fy', FinancialYear::forDate());
                $windowOpen = $this->incomeTaxService->declarationWindowOpen((int) $companyId);
                $declaration = $this->incomeTaxService->findDeclaration((int) $companyId, (int) $user->id, $fy);
                $regime = $declaration?->regime ?? 'new';

                $data['taxDeclaration'] = $declaration;
                $data['taxSections'] = $this->incomeTaxService->sectionsForRegime($regime);
                $data['taxProjection'] = $declaration
                    ? $this->incomeTaxService->projectTax($declaration)
                    : $this->incomeTaxService->previewProjection((int) $companyId, (int) $user->id, 'new', [], $fy);
                $data['taxWindowOpen'] = $windowOpen;
                $data['taxFinancialYear'] = $fy;
                $data['taxSettings'] = [
                    'proof_mandatory' => $this->getSetting((int) $companyId, 'proof_mandatory', '0'),
                ];
                $data['taxUiState'] = $declaration
                    ? $declaration->uiState($windowOpen)
                    : [
                        'mode' => 'edit',
                        'label' => 'Not started',
                        'hint' => 'Choose regime and save to start your declaration.',
                        'can_edit' => $windowOpen,
                        'can_save' => $windowOpen,
                        'can_submit' => false,
                        'can_upload_proofs' => false,
                    ];
            }
        }

        if ($tab === 'summary') {
            $employee = $user;

            $latestEmployeePayroll = EmployeePayroll::withoutGlobalScopes()
                ->where('company_id', $companyId)
                ->where('user_id', (int) $employee->id)
                ->with('payrollCycle')
                ->orderByDesc('id')
                ->first();

            $latestLegacyPayroll = null;
            if (! $latestEmployeePayroll) {
                $latestLegacyPayroll = Payroll::withoutGlobalScopes()
                    ->where('company_id', $companyId)
                    ->where('user_id', (int) $employee->id)
                    ->orderByDesc('year')
                    ->orderByDesc('month')
                    ->first();
            }

            $lastProcessedCycle = null;
            $workingDays = null;
            $lopDays = null;
            $payslipUrl = null;

            if ($latestEmployeePayroll) {
                $cycle = $latestEmployeePayroll->payrollCycle;
                if ($cycle) {
                    $start = $cycle->start_date?->format('j M');
                    $end = $cycle->end_date?->format('j M');
                    $lastProcessedCycle = trim(($start && $end ? "{$start} - {$end}" : '') . ' ' . ($cycle->year ?? ''));
                }
                $workingDays = $latestEmployeePayroll->working_days ?: null;
                $lopDays = $latestEmployeePayroll->lop_days;
                $payslipUrl = route('payroll.my.payslips.show', $latestEmployeePayroll);
            } elseif ($latestLegacyPayroll) {
                $month = date('M', mktime(0, 0, 0, (int) $latestLegacyPayroll->month, 1));
                $lastProcessedCycle = "{$month} {$latestLegacyPayroll->year}";
                $workingDays = $latestLegacyPayroll->working_days ?: null;
                $lopDays = (float) max(0, ((float) ($latestLegacyPayroll->total_days ?? 0) - (float) ($latestLegacyPayroll->present_days ?? 0)));
                $payslipUrl = route('payroll.my.legacy-payslips.show', $latestLegacyPayroll);
            }

            $data['summaryData'] = [
                'payroll' => [
                    'last_processed_cycle' => $lastProcessedCycle,
                    'working_days' => $workingDays,
                    'lop_days' => $lopDays,
                    'payslip_url' => $payslipUrl,
                ],
                'payment' => [
                    'mode' => str_replace('_', ' ', $this->getSetting((int) $companyId, 'payout_mode', 'bank_transfer')),
                    'bank_name' => $employee->bank_name,
                    'account_number' => $employee->account_number,
                    'ifsc_code' => $employee->ifsc_code,
                    'name_on_account' => $employee->account_holder_name ?: $employee->name,
                    'branch' => $employee->branch_name,
                ],
                'identity' => [
                    'pan_number' => $employee->pan_number,
                    'name' => $employee->name,
                    'dob' => $employee->dob,
                    'father_name' => $employee->father_name,
                ],
                'statutory' => [
                    'pf_status' => $this->getSetting((int) $companyId, 'payroll_pf_enabled', '0') === '1' ? 'Enabled' : 'Disabled',
                    'uan_number' => $employee->uan_number,
                    'pf_join_date' => $employee->pf_join_date,
                    'name' => $employee->name,
                    'pt_state' => $employee->work_state,
                    'pt_location' => $employee->pt_location,
                ],
            ];
        }

        return view('payroll.home', $data);
    }

    /** @return array<int, 'completed'|'pending'|'future'> */
    protected function getLegacyMonthStatuses(int $companyId, int $year): array
    {
        $currentYear = (int) date('Y');
        $currentMonth = (int) date('n');
        $statuses = [];
        for ($m = 1; $m <= 12; $m++) {
            if ($year > $currentYear || ($year === $currentYear && $m > $currentMonth)) {
                $statuses[$m] = 'future';
                continue;
            }
            $count = Payroll::withoutGlobalScopes()
                ->where('company_id', $companyId)
                ->where('year', $year)
                ->where('month', $m)
                ->count();
            $paidCount = Payroll::withoutGlobalScopes()
                ->where('company_id', $companyId)
                ->where('year', $year)
                ->where('month', $m)
                ->where('status', 'paid')
                ->count();
            $statuses[$m] = ($count > 0 && $paidCount === $count) ? 'completed' : 'pending';
        }
        return $statuses;
    }

    protected function getSetting(int $companyId, string $key, string $default): string
    {
        $row = CompanySetting::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->where('key', $key)
            ->first();
        return $row ? (string) $row->value : $default;
    }

    /**
     * @return \Illuminate\Support\Collection<int, array{effective_date:string, regular:float, other:float, bonus:float, total:float, view_url:null}>
     */
    protected function buildSalaryTimelineFallbackFromPayroll(int $companyId, int $userId)
    {
        $rows = EmployeePayroll::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->where('user_id', $userId)
            ->with('payrollCycle')
            ->orderByDesc('id')
            ->limit(10)
            ->get()
            ->map(function (EmployeePayroll $ep) {
                $gross = (float) ($ep->gross_monthly ?? 0);
                $net = (float) ($ep->net_monthly ?? 0);
                $deductions = max(0, $gross - $net);
                $bonus = 0.0;

                return [
                    'effective_date' => $ep->payrollCycle
                        ? sprintf('01 %s %s', date('M', mktime(0, 0, 0, (int) $ep->payrollCycle->month, 1)), $ep->payrollCycle->year)
                        : '—',
                    'regular' => max(0, $gross - $bonus),
                    'other' => $deductions,
                    'bonus' => $bonus,
                    'total' => $gross,
                    'view_url' => null,
                ];
            });

        if ($rows->isNotEmpty()) {
            return $rows;
        }

        return Payroll::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->where('user_id', $userId)
            ->orderByDesc('year')
            ->orderByDesc('month')
            ->limit(10)
            ->get()
            ->map(function (Payroll $payroll) {
                $gross = (float) ($payroll->salary_amount ?? 0);
                $net = (float) ($payroll->net_salary ?? 0);
                $deductions = max(0, $gross - $net);

                return [
                    'effective_date' => sprintf('01 %s %s', date('M', mktime(0, 0, 0, (int) $payroll->month, 1)), $payroll->year),
                    'regular' => max(0, $gross - $deductions),
                    'other' => $deductions,
                    'bonus' => 0.0,
                    'total' => $gross,
                    'view_url' => null,
                ];
            });
    }

    protected function buildMyPayslipPreviewFromV2(EmployeePayroll $employeePayroll): array
    {
        $cycle = $employeePayroll->payrollCycle;
        $employee = $employeePayroll->user;
        $company = $employee?->company;
        $earnings = $employeePayroll->components->where('category', 'earning');
        $deductions = $employeePayroll->components->where('category', 'deduction');

        $totalEarnings = (float) ($employeePayroll->gross_monthly ?? 0);
        $totalDeductions = (float) ($employeePayroll->total_deductions ?? abs($deductions->sum('amount')));
        $net = (float) ($employeePayroll->net_monthly ?? 0);
        $period = ($cycle && (int) $cycle->year > 0 && (int) $cycle->month > 0)
            ? Carbon::createFromDate((int) $cycle->year, (int) $cycle->month, 1)->format('F Y')
            : 'N/A';

        return [
            'source' => 'v2',
            'source_id' => (int) $employeePayroll->id,
            'source_year' => (int) ($cycle?->year ?? 0),
            'period' => $period,
            'view_url' => route('payroll.my.payslips.show', $employeePayroll),
            'pdf_url' => route('payroll.my.payslips.download', $employeePayroll),
            'company' => [
                'name' => $company?->name ?? config('app.name'),
                'address' => $company?->address,
                'email' => $company?->email,
                'phone' => $company?->phone,
                'logo_url' => !empty($company?->logo) ? asset('storage/' . ltrim((string) $company->logo, '/')) : null,
            ],
            'employee' => [
                'name' => $employee?->name ?? 'Employee',
                'employee_id' => $employee?->employee_number ?: ('EMP-' . str_pad((string) ($employee?->id ?? 0), 3, '0', STR_PAD_LEFT)),
                'designation' => $employee?->designation?->name ?? 'N/A',
                'department' => $employee?->department?->name ?? 'N/A',
                'location' => $employee?->location?->name ?? 'N/A',
                'bank_name' => $employee?->bank_name ?? data_get($employee, 'bankName') ?? 'N/A',
                'bank_account_no' => $employee?->account_number ?? data_get($employee, 'bank_account_no') ?? data_get($employee, 'bankAccountNo') ?? 'N/A',
                'ifsc_code' => $employee?->ifsc_code ?? data_get($employee, 'ifscCode') ?? 'N/A',
                'pan_no' => data_get($employee, 'pan_no') ?? data_get($employee, 'pan_number') ?? data_get($employee, 'panNo') ?? 'N/A',
                'work_days' => (int) ($employeePayroll->working_days ?? 0),
                'lop_days' => (float) ($employeePayroll->lop_days ?? 0),
            ],
            'earnings' => $earnings->map(fn ($item) => [
                'name' => (string) ($item->name ?? 'Earning'),
                'amount' => (float) ($item->amount ?? 0),
            ])->values()->all(),
            'deductions' => $deductions->map(fn ($item) => [
                'name' => (string) ($item->name ?? 'Deduction'),
                'amount' => abs((float) ($item->amount ?? 0)),
            ])->values()->all(),
            'totals' => [
                'earnings' => $totalEarnings,
                'deductions' => $totalDeductions,
                'net' => $net,
                'net_in_words' => $this->formatCurrencyToWords($net),
            ],
        ];
    }

    protected function buildMyPayslipPreviewFromLegacy(Payroll $payroll): array
    {
        $employee = $payroll->user;
        $company = $employee?->company;
        $earnings = $payroll->payrollComponents->where('is_earning', true);
        $deductions = $payroll->payrollComponents->where('is_earning', false);

        $totalEarnings = (float) ($payroll->salary_amount ?? 0);
        $totalDeductions = max(0, round($totalEarnings - (float) ($payroll->net_salary ?? 0), 2));
        $net = (float) ($payroll->net_salary ?? 0);
        $period = ((int) ($payroll->year ?? 0) > 0 && (int) ($payroll->month ?? 0) > 0)
            ? Carbon::createFromDate((int) $payroll->year, (int) $payroll->month, 1)->format('F Y')
            : 'N/A';

        return [
            'source' => 'legacy',
            'source_id' => (int) $payroll->id,
            'source_year' => (int) ($payroll->year ?? 0),
            'period' => $period,
            'view_url' => route('payroll.my.legacy-payslips.show', $payroll),
            'pdf_url' => route('payroll.my.legacy-payslips.download', $payroll),
            'company' => [
                'name' => $company?->name ?? config('app.name'),
                'address' => $company?->address,
                'email' => $company?->email,
                'phone' => $company?->phone,
                'logo_url' => !empty($company?->logo) ? asset('storage/' . ltrim((string) $company->logo, '/')) : null,
            ],
            'employee' => [
                'name' => $employee?->name ?? 'Employee',
                'employee_id' => $employee?->employee_number ?: ('EMP-' . str_pad((string) ($employee?->id ?? 0), 3, '0', STR_PAD_LEFT)),
                'designation' => $employee?->designation?->name ?? 'N/A',
                'department' => $employee?->department?->name ?? 'N/A',
                'location' => $employee?->location?->name ?? 'N/A',
                'bank_name' => $employee?->bank_name ?? data_get($employee, 'bankName') ?? 'N/A',
                'bank_account_no' => $employee?->account_number ?? data_get($employee, 'bank_account_no') ?? data_get($employee, 'bankAccountNo') ?? 'N/A',
                'ifsc_code' => $employee?->ifsc_code ?? data_get($employee, 'ifscCode') ?? 'N/A',
                'pan_no' => data_get($employee, 'pan_no') ?? data_get($employee, 'pan_number') ?? data_get($employee, 'panNo') ?? 'N/A',
                'work_days' => (int) ($payroll->working_days ?? 0),
                'lop_days' => (float) ($payroll->unpaid_leaves ?? 0),
            ],
            'earnings' => $earnings->map(fn ($item) => [
                'name' => (string) ($item->name ?? 'Earning'),
                'amount' => (float) ($item->amount ?? 0),
            ])->values()->all(),
            'deductions' => $deductions->map(fn ($item) => [
                'name' => (string) ($item->name ?? 'Deduction'),
                'amount' => abs((float) ($item->amount ?? 0)),
            ])->values()->all(),
            'totals' => [
                'earnings' => $totalEarnings,
                'deductions' => $totalDeductions,
                'net' => $net,
                'net_in_words' => $this->formatCurrencyToWords($net),
            ],
        ];
    }

    protected function formatCurrencyToWords(float $amount): string
    {
        $value = round(max(0, $amount), 2);
        $rupees = (int) floor($value);
        $paise = (int) round(($value - $rupees) * 100);

        if (! class_exists(\NumberFormatter::class)) {
            return number_format($value, 2) . ' rupees only';
        }

        $formatter = new \NumberFormatter('en_IN', \NumberFormatter::SPELLOUT);
        $rupeesWords = ucfirst(trim((string) $formatter->format($rupees)));
        $words = $rupeesWords . ' rupees';
        if ($paise > 0) {
            $paiseWords = trim((string) $formatter->format($paise));
            $words .= ' and ' . $paiseWords . ' paise';
        }

        return $words . ' only';
    }
}

