<?php

namespace App\Services\Payroll;

use App\Models\SalaryRevision;
use App\Models\SalaryGroup;
use App\Models\User;
use Carbon\Carbon;

class SalaryStructureResolver
{
    private function resolveRowValueType($sgc): string
    {
        if (! empty($sgc->value_type)) {
            return (string) $sgc->value_type;
        }

        return $sgc->percentage !== null ? 'basic_percent' : 'fixed';
    }

    private function resolveRowValue($sgc): float
    {
        if ($sgc->value !== null) {
            return (float) $sgc->value;
        }
        if ($sgc->percentage !== null) {
            return (float) $sgc->percentage;
        }

        return (float) ($sgc->amount ?? 0);
    }

    public function getEffectiveRevision(int $userId, int $companyId, Carbon $periodEnd, int $salaryRevisionId = null): ?SalaryRevision
    {
        $query = SalaryRevision::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->where('user_id', $userId);

        if (!is_null($salaryRevisionId)) {
            return $query->where('id', $salaryRevisionId)->first();
        }

        return $query
            ->whereDate('effective_date', '<=', $periodEnd->toDateString())
            ->orderByDesc('effective_date')
            ->orderByDesc('id')
            ->first();
    }

    /**
     * @param mixed $components
     * @return array<int, array{name:string, annual:float}>
     */
    public function normalizeRevisionComponents($components): array
    {
        if (! is_array($components)) {
            return [];
        }

        $rows = [];
        foreach ($components as $component) {
            if (! is_array($component)) {
                continue;
            }
            $name = trim((string) ($component['name'] ?? ''));
            if ($name === '') {
                continue;
            }
            $annual = max(0, round((float) ($component['annual'] ?? 0), 2));
            $rows[] = [
                'name' => $name,
                'type' => $component['type'] ?? 'earning',
                'value' => $component['value'] ?? 0,
                'annual' => $annual,
                'monthly' => $component['monthly'] ?? round($annual / 12, 2),
                'value_type' => $component['value_type'] ?? 'fixed',
            ];
        }

        return $rows;
    }

    /**
     * @return array<int, array{name:string, type:string, amount:float}>
     */
    public function resolveMonthlyComponents(User $user, int $companyId, Carbon $periodEnd): array
    {
        $revision = $this->getEffectiveRevision($user->id, $companyId, $periodEnd);
        $normalizedRevision = $this->normalizeRevisionComponents($revision?->components_json);
        if (! empty($normalizedRevision)) {
            return array_map(
                fn (array $row) => [
                    'name' => $row['name'],
                    'type' => 'earning',
                    'value_type' => $row['value_type'] ?? 'fixed',
                    'value' => $row['value'] ?? 0,                    
                    'annual' => round($row['annual'] / 12, 2),
                    'monthly' => $row['monthly'] ?? round($row['annual'] / 12, 2),
                ],
                $normalizedRevision
            );
        }

        $group = $user->salary_group_id
            ? SalaryGroup::withoutGlobalScopes()->with('salaryGroupComponents.salaryComponent')->find($user->salary_group_id)
            : null;

        if (! $group || $group->salaryGroupComponents->isEmpty()) {
            return [];
        }

        $basicBase = $this->resolveMonthlyBasic($user, $companyId, $periodEnd);
        $ctcBase = 0.0;
        $rows = [];
        foreach ($group->salaryGroupComponents as $sgc) {
            $component = $sgc->salaryComponent;
            if (! $component) {
                continue;
            }

            $valueType = $this->resolveRowValueType($sgc);
            $value = $this->resolveRowValue($sgc);
            $amount = 0.0;
            if (in_array($valueType, ['fixed', 'variable'], true)) {
                $amount = $value;
            } elseif ($valueType === 'basic_percent') {
                $amount = round(((float) $basicBase) * ($value / 100), 2);
            } elseif ($valueType === 'ctc_percent') {
                $amount = round($ctcBase * ($value / 100), 2);
            } elseif ($valueType === 'balancing_ctc') {
                $amount = round(max(0, $value - $ctcBase), 2);
            } elseif ($valueType === 'system_calculated') {
                $amount = (float) ($component->default_amount ?? 0);
            }

            if (($component->type ?? '') === 'earning') {
                $ctcBase += max(0, $amount);
            }

            $rows[] = [
                'name' => (string) ($component->name ?? 'Component'),
                'type' => (string) ($component->type ?? 'earning'),
                'amount' => round(max(0, $amount), 2),
            ];
        }

        return $rows;
    }

    public function resolveMonthlyBasic(User $user, int $companyId, Carbon $periodEnd): ?float
    {
        $revision = $this->getEffectiveRevision($user->id, $companyId, $periodEnd);
        $normalizedRevision = $this->normalizeRevisionComponents($revision?->components_json);
        foreach ($normalizedRevision as $row) {
            if (stripos($row['name'], 'basic') !== false) {
                return round($row['annual'] / 12, 2);
            }
        }

        $group = $user->salary_group_id
            ? SalaryGroup::withoutGlobalScopes()->with('salaryGroupComponents.salaryComponent')->find($user->salary_group_id)
            : null;
        if (! $group || $group->salaryGroupComponents->isEmpty()) {
            return null;
        }

        foreach ($group->salaryGroupComponents as $sgc) {
            $component = $sgc->salaryComponent;
            if ($component && ($component->type ?? '') === 'earning' && stripos((string) ($component->name ?? ''), 'basic') !== false) {
                return round($this->resolveRowValue($sgc), 2);
            }
        }

        return null;
    }
}
