<?php

namespace App\Services;

use App\Models\Employee;
use App\Models\EmployeeSalaryLine;
use App\Models\PayrollVoucher;
use App\Support\PayrollVoucherOptions;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;

class PayrollSheetService
{
    public function defaultGeneratorFilters(): array
    {
        return [
            'voucher_type' => 'salary_sheet',
            'payroll_year' => (int) date('Y'),
            'payroll_month' => (int) date('n'),
            'department' => '',
            'designation' => '',
            'project' => '',
            'branch' => '',
            'religion' => '',
        ];
    }

    public function normalizeGeneratorFilters(array $input): array
    {
        $defaults = $this->defaultGeneratorFilters();

        return [
            'voucher_type'  => array_key_exists($input['voucher_type'] ?? '', PayrollVoucherOptions::types())
                ? $input['voucher_type']
                : $defaults['voucher_type'],
            'payroll_year'  => max(2000, min(2100, (int) ($input['payroll_year'] ?? $defaults['payroll_year']))),
            'payroll_month' => max(1, min(12, (int) ($input['payroll_month'] ?? $defaults['payroll_month']))),
            'department'    => trim((string) ($input['department'] ?? '')),
            'designation'   => trim((string) ($input['designation'] ?? '')),
            'project'       => trim((string) ($input['project'] ?? '')),
            'branch'        => trim((string) ($input['branch'] ?? '')),
            'religion'      => trim((string) ($input['religion'] ?? '')),
        ];
    }

    public function employeesQuery(int $companyId, array $filters): Builder
    {
        $query = Employee::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->whereNull('deleted_at')
            ->where('is_active', true)
            ->where('terminated_employee', false);

        if ($filters['department'] !== '') {
            $query->where('department', $filters['department']);
        }
        if ($filters['designation'] !== '') {
            $query->where('designation', $filters['designation']);
        }
        if ($filters['project'] !== '') {
            $query->where('project', $filters['project']);
        }
        if ($filters['branch'] !== '') {
            $query->where('branch', $filters['branch']);
        }
        if ($filters['religion'] !== '') {
            $query->where('religion', $filters['religion']);
        }

        return $query->orderBy('code')->orderBy('name');
    }

    public function matchedEmployees(int $companyId, array $filters): Collection
    {
        return $this->employeesQuery($companyId, $filters)
            ->with(['salaryLines.salaryComponent'])
            ->get();
    }

    public function buildSheet(int $companyId, array $filters): array
    {
        $config = $this->groupedSheetConfig($filters['voucher_type']);
        if ($config) {
            return $this->buildGroupedAmountSheet($companyId, $filters, $config);
        }

        return $this->buildSimpleSheet($companyId, $filters);
    }

    private function groupedSheetConfig(string $voucherType): ?array
    {
        return match ($voucherType) {
            'bonus_sheet' => [
                'amount_label'          => 'Festival Bonus Amount',
                'payment_summary_title' => 'Payment Method Summary',
                'show_religion_filter'  => true,
                'empty_message'         => 'No employees with festival bonus amounts match the selected filters.',
            ],
            'pf_company_sheet' => [
                'amount_label'          => 'PF (Company) Amount',
                'payment_summary_title' => 'PF (Company) Amount by Payment Method',
                'show_religion_filter'  => false,
                'empty_message'         => 'No employees with PF (Company) amounts match the selected filters.',
            ],
            'gratuity_sheet' => [
                'amount_label'          => 'Gratuity Amount',
                'payment_summary_title' => 'Gratuity Amount by Payment Method',
                'show_religion_filter'  => true,
                'empty_message'         => 'No employees with gratuity amounts match the selected filters.',
            ],
            default => null,
        };
    }

    private function buildSimpleSheet(int $companyId, array $filters): array
    {
        $employees = $this->matchedEmployees($companyId, $filters);
        $rows = [];
        $totalEarnings = 0;
        $totalDeductions = 0;

        foreach ($employees as $employee) {
            $amounts = $this->amountsForEmployee($employee, $filters['voucher_type']);
            if ($amounts['earnings'] <= 0 && $amounts['deductions'] <= 0) {
                continue;
            }

            $rows[] = [
                'employee'   => $employee,
                'earnings'   => $amounts['earnings'],
                'deductions' => $amounts['deductions'],
                'net'        => $amounts['net'],
            ];
            $totalEarnings += $amounts['earnings'];
            $totalDeductions += $amounts['deductions'];
        }

        return [
            'layout'           => 'simple',
            'filters'          => $filters,
            'employees'        => $rows,
            'employee_count'   => count($rows),
            'total_earnings'   => round($totalEarnings, 2),
            'total_deductions' => round($totalDeductions, 2),
            'total_net'        => round($totalEarnings - $totalDeductions, 2),
            'period_label'     => PayrollVoucherOptions::periodLabel($filters['payroll_year'], $filters['payroll_month']),
            'type_label'       => PayrollVoucherOptions::typeLabel($filters['voucher_type']),
        ];
    }

    private function buildGroupedAmountSheet(int $companyId, array $filters, array $config): array
    {
        $employees = $this->matchedEmployees($companyId, $filters);
        $voucherType = $filters['voucher_type'];
        $departmentGroups = [];
        $departmentSummary = [];
        $paymentSummary = [];
        $employeeRows = [];
        $grandTotal = 0.0;

        foreach ($employees as $employee) {
            $amount = $this->amountsForEmployee($employee, $voucherType)['earnings'];
            if ($amount <= 0) {
                continue;
            }

            $row = ['employee' => $employee, 'amount' => $amount];
            $employeeRows[] = $row;

            $dept = trim((string) $employee->department) !== '' ? trim($employee->department) : 'Unassigned';
            if (!isset($departmentGroups[$dept])) {
                $departmentGroups[$dept] = [
                    'name'     => $dept,
                    'rows'     => [],
                    'subtotal' => ['amount' => 0.0],
                ];
            }
            $departmentGroups[$dept]['rows'][] = $row;
            $departmentGroups[$dept]['subtotal']['amount'] = round(
                $departmentGroups[$dept]['subtotal']['amount'] + $amount,
                2,
            );

            if (!isset($departmentSummary[$dept])) {
                $departmentSummary[$dept] = [
                    'department'     => $dept,
                    'employee_count' => 0,
                    'amount'         => 0.0,
                ];
            }
            $departmentSummary[$dept]['employee_count']++;
            $departmentSummary[$dept]['amount'] = round($departmentSummary[$dept]['amount'] + $amount, 2);

            $method = trim((string) $employee->salary_payment_method) !== ''
                ? trim($employee->salary_payment_method)
                : '—';
            if (!isset($paymentSummary[$method])) {
                $paymentSummary[$method] = [
                    'method'           => $method,
                    'employee_count' => 0,
                    'amount'           => 0.0,
                ];
            }
            $paymentSummary[$method]['employee_count']++;
            $paymentSummary[$method]['amount'] = round($paymentSummary[$method]['amount'] + $amount, 2);

            $grandTotal = round($grandTotal + $amount, 2);
        }

        ksort($departmentGroups);
        ksort($departmentSummary);
        ksort($paymentSummary);

        return [
            'layout'                => 'grouped_voucher_sheet',
            'filters'               => $filters,
            'employees'             => $employeeRows,
            'employee_count'        => count($employeeRows),
            'amount_label'          => $config['amount_label'],
            'payment_summary_title' => $config['payment_summary_title'],
            'show_religion_filter'  => $config['show_religion_filter'],
            'empty_message'         => $config['empty_message'],
            'department_groups'     => array_values($departmentGroups),
            'department_summary'    => array_values($departmentSummary),
            'payment_summary'       => array_values($paymentSummary),
            'grand_total'           => $grandTotal,
            'total_earnings'        => $grandTotal,
            'total_deductions'      => 0.0,
            'total_net'             => $grandTotal,
            'period_label'          => PayrollVoucherOptions::periodLabel($filters['payroll_year'], $filters['payroll_month']),
            'type_label'            => PayrollVoucherOptions::typeLabel($filters['voucher_type']),
        ];
    }

    public function filterStatusLabel(array $sheet): string
    {
        if ($sheet['is_draft'] ?? false) {
            return 'draft';
        }

        return strtolower(PayrollVoucherOptions::statusLabel($sheet['status'] ?? 'approved'));
    }

    public function amountsForEmployee(Employee $employee, string $voucherType): array
    {
        $earnings = 0;
        $deductions = 0;
        $subTypes = PayrollVoucherOptions::componentSubTypesFor($voucherType);

        foreach ($employee->salaryLines as $line) {
            if (!$this->lineMatchesVoucherType($line, $voucherType, $subTypes)) {
                continue;
            }

            if (in_array($line->line_type, ['earning', 'irregular'], true)) {
                $earnings += (float) $line->amount;
            } elseif ($line->line_type === 'deduction') {
                $deductions += (float) $line->amount;
            }
        }

        return [
            'earnings'   => round($earnings, 2),
            'deductions' => round($deductions, 2),
            'net'        => round($earnings - $deductions, 2),
        ];
    }

    private function lineMatchesVoucherType(EmployeeSalaryLine $line, string $voucherType, ?array $subTypes): bool
    {
        if ($voucherType === 'salary_sheet') {
            return in_array($line->line_type, ['earning', 'deduction'], true);
        }

        if ($line->line_type === 'deduction') {
            return false;
        }

        if (!$subTypes) {
            return false;
        }

        $componentSubType = $line->salaryComponent?->sub_type;

        return $componentSubType && in_array($componentSubType, $subTypes, true);
    }

    public function nextVoucherNo(int $companyId, int $year, int $month): string
    {
        $monthLabel = PayrollVoucherOptions::monthLabel($month);
        $prefix = sprintf('PAY-%d-%s-', $year, $monthLabel);

        $latest = PayrollVoucher::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->where('voucher_no', 'like', $prefix . '%')
            ->orderByDesc('voucher_no')
            ->value('voucher_no');

        $seq = 1;
        if ($latest && preg_match('/-(\d+)$/', $latest, $matches)) {
            $seq = (int) $matches[1] + 1;
        }

        return $prefix . str_pad((string) $seq, 4, '0', STR_PAD_LEFT);
    }

    public function duplicateExists(int $companyId, array $filters, ?int $ignoreId = null): bool
    {
        $query = PayrollVoucher::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->where('voucher_type', $filters['voucher_type'])
            ->where('payroll_year', $filters['payroll_year'])
            ->where('payroll_month', $filters['payroll_month'])
            ->whereIn('status', ['pending_approval', 'approved'])
            ->where('department', $filters['department'] ?: null)
            ->where('designation', $filters['designation'] ?: null)
            ->where('project', $filters['project'] ?: null)
            ->where('branch', $filters['branch'] ?: null)
            ->where('religion', $filters['religion'] ?: null);

        if ($ignoreId) {
            $query->where('id', '!=', $ignoreId);
        }

        return $query->exists();
    }

    public function createVoucher(int $companyId, array $filters, int $userId): PayrollVoucher
    {
        $sheet = $this->buildSheet($companyId, $filters);

        return PayrollVoucher::withoutGlobalScopes()->create([
            'company_id'       => $companyId,
            'voucher_no'       => $this->nextVoucherNo($companyId, $filters['payroll_year'], $filters['payroll_month']),
            'voucher_type'     => $filters['voucher_type'],
            'payroll_year'     => $filters['payroll_year'],
            'payroll_month'    => $filters['payroll_month'],
            'department'       => $filters['department'] ?: null,
            'designation'      => $filters['designation'] ?: null,
            'project'          => $filters['project'] ?: null,
            'branch'           => $filters['branch'] ?: null,
            'religion'         => $filters['religion'] ?: null,
            'employee_count'   => $sheet['employee_count'],
            'total_earnings'   => $sheet['total_earnings'],
            'total_deductions' => $sheet['total_deductions'],
            'total_net'        => $sheet['total_net'],
            'status'           => 'pending_approval',
            'submitted_by'     => $userId,
            'submitted_at'     => now(),
        ]);
    }

    public function sheetFromVoucher(PayrollVoucher $voucher): array
    {
        $filters = [
            'voucher_type'  => $voucher->voucher_type,
            'payroll_year'  => $voucher->payroll_year,
            'payroll_month' => $voucher->payroll_month,
            'department'    => $voucher->department ?? '',
            'designation'   => $voucher->designation ?? '',
            'project'       => $voucher->project ?? '',
            'branch'        => $voucher->branch ?? '',
            'religion'      => $voucher->religion ?? '',
        ];

        $sheet = $this->buildSheet($voucher->company_id, $filters);
        $sheet['voucher_no'] = $voucher->voucher_no;
        $sheet['status'] = $voucher->status;
        $sheet['is_draft'] = false;

        return $sheet;
    }
}
