<?php

namespace App\Services;

use App\Models\Tenants\Payroll;
use App\Models\Tenants\PayrollSetting;
use App\Models\Tenants\Withdrawal;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

class PayrollService extends Service
{
    // =========================================================================
    // 급여 목록/상세
    // =========================================================================

    /**
     * 급여 목록
     */
    public function index(array $params): LengthAwarePaginator
    {
        $tenantId = $this->tenantId();

        $query = Payroll::query()
            ->where('tenant_id', $tenantId)
            ->with(['user:id,name,email', 'creator:id,name']);

        // 연도 필터
        if (! empty($params['year'])) {
            $query->where('pay_year', $params['year']);
        }

        // 월 필터
        if (! empty($params['month'])) {
            $query->where('pay_month', $params['month']);
        }

        // 사용자 필터
        if (! empty($params['user_id'])) {
            $query->where('user_id', $params['user_id']);
        }

        // 상태 필터
        if (! empty($params['status'])) {
            $query->where('status', $params['status']);
        }

        // 검색 (사용자명)
        if (! empty($params['search'])) {
            $query->whereHas('user', function ($q) use ($params) {
                $q->where('name', 'like', "%{$params['search']}%");
            });
        }

        // 정렬
        $sortBy = $params['sort_by'] ?? 'pay_year';
        $sortDir = $params['sort_dir'] ?? 'desc';

        if ($sortBy === 'period') {
            $query->orderBy('pay_year', $sortDir)->orderBy('pay_month', $sortDir);
        } else {
            $query->orderBy($sortBy, $sortDir);
        }

        $perPage = $params['per_page'] ?? 20;

        return $query->paginate($perPage);
    }

    /**
     * 특정 연월 급여 요약
     */
    public function summary(int $year, int $month): array
    {
        $tenantId = $this->tenantId();

        $stats = Payroll::query()
            ->where('tenant_id', $tenantId)
            ->where('pay_year', $year)
            ->where('pay_month', $month)
            ->selectRaw('
                COUNT(*) as total_count,
                SUM(CASE WHEN status = ? THEN 1 ELSE 0 END) as draft_count,
                SUM(CASE WHEN status = ? THEN 1 ELSE 0 END) as confirmed_count,
                SUM(CASE WHEN status = ? THEN 1 ELSE 0 END) as paid_count,
                SUM(gross_salary) as total_gross,
                SUM(total_deductions) as total_deductions,
                SUM(net_salary) as total_net
            ', [Payroll::STATUS_DRAFT, Payroll::STATUS_CONFIRMED, Payroll::STATUS_PAID])
            ->first();

        return [
            'year' => $year,
            'month' => $month,
            'total_count' => (int) $stats->total_count,
            'draft_count' => (int) $stats->draft_count,
            'confirmed_count' => (int) $stats->confirmed_count,
            'paid_count' => (int) $stats->paid_count,
            'total_gross' => (float) $stats->total_gross,
            'total_deductions' => (float) $stats->total_deductions,
            'total_net' => (float) $stats->total_net,
        ];
    }

    /**
     * 급여 상세
     */
    public function show(int $id): Payroll
    {
        $tenantId = $this->tenantId();

        return Payroll::query()
            ->where('tenant_id', $tenantId)
            ->with([
                'user:id,name,email',
                'confirmer:id,name',
                'withdrawal',
                'creator:id,name',
            ])
            ->findOrFail($id);
    }

    // =========================================================================
    // 급여 생성/수정/삭제
    // =========================================================================

    /**
     * 급여 생성
     */
    public function store(array $data): Payroll
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        // 중복 확인
        $exists = Payroll::query()
            ->where('tenant_id', $tenantId)
            ->where('user_id', $data['user_id'])
            ->where('pay_year', $data['pay_year'])
            ->where('pay_month', $data['pay_month'])
            ->exists();

        if ($exists) {
            throw new BadRequestHttpException(__('error.payroll.already_exists'));
        }

        // 금액 계산
        $grossSalary = $this->calculateGross($data);
        $totalDeductions = $this->calculateDeductions($data);
        $netSalary = $grossSalary - $totalDeductions;

        return Payroll::create([
            'tenant_id' => $tenantId,
            'user_id' => $data['user_id'],
            'pay_year' => $data['pay_year'],
            'pay_month' => $data['pay_month'],
            'base_salary' => $data['base_salary'] ?? 0,
            'overtime_pay' => $data['overtime_pay'] ?? 0,
            'bonus' => $data['bonus'] ?? 0,
            'allowances' => $data['allowances'] ?? null,
            'gross_salary' => $grossSalary,
            'income_tax' => $data['income_tax'] ?? 0,
            'resident_tax' => $data['resident_tax'] ?? 0,
            'health_insurance' => $data['health_insurance'] ?? 0,
            'pension' => $data['pension'] ?? 0,
            'employment_insurance' => $data['employment_insurance'] ?? 0,
            'deductions' => $data['deductions'] ?? null,
            'total_deductions' => $totalDeductions,
            'net_salary' => $netSalary,
            'status' => Payroll::STATUS_DRAFT,
            'note' => $data['note'] ?? null,
            'created_by' => $userId,
            'updated_by' => $userId,
        ]);
    }

    /**
     * 급여 수정
     */
    public function update(int $id, array $data): Payroll
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        $payroll = Payroll::query()
            ->where('tenant_id', $tenantId)
            ->findOrFail($id);

        if (! $payroll->isEditable()) {
            throw new BadRequestHttpException(__('error.payroll.not_editable'));
        }

        // 연월 변경 시 중복 확인
        $newYear = $data['pay_year'] ?? $payroll->pay_year;
        $newMonth = $data['pay_month'] ?? $payroll->pay_month;
        $newUserId = $data['user_id'] ?? $payroll->user_id;

        if ($newYear != $payroll->pay_year || $newMonth != $payroll->pay_month || $newUserId != $payroll->user_id) {
            $exists = Payroll::query()
                ->where('tenant_id', $tenantId)
                ->where('user_id', $newUserId)
                ->where('pay_year', $newYear)
                ->where('pay_month', $newMonth)
                ->where('id', '!=', $id)
                ->exists();

            if ($exists) {
                throw new BadRequestHttpException(__('error.payroll.already_exists'));
            }
        }

        // 금액 업데이트
        $updateData = array_merge($payroll->toArray(), $data);
        $grossSalary = $this->calculateGross($updateData);
        $totalDeductions = $this->calculateDeductions($updateData);
        $netSalary = $grossSalary - $totalDeductions;

        $payroll->fill([
            'user_id' => $data['user_id'] ?? $payroll->user_id,
            'pay_year' => $data['pay_year'] ?? $payroll->pay_year,
            'pay_month' => $data['pay_month'] ?? $payroll->pay_month,
            'base_salary' => $data['base_salary'] ?? $payroll->base_salary,
            'overtime_pay' => $data['overtime_pay'] ?? $payroll->overtime_pay,
            'bonus' => $data['bonus'] ?? $payroll->bonus,
            'allowances' => $data['allowances'] ?? $payroll->allowances,
            'gross_salary' => $grossSalary,
            'income_tax' => $data['income_tax'] ?? $payroll->income_tax,
            'resident_tax' => $data['resident_tax'] ?? $payroll->resident_tax,
            'health_insurance' => $data['health_insurance'] ?? $payroll->health_insurance,
            'pension' => $data['pension'] ?? $payroll->pension,
            'employment_insurance' => $data['employment_insurance'] ?? $payroll->employment_insurance,
            'deductions' => $data['deductions'] ?? $payroll->deductions,
            'total_deductions' => $totalDeductions,
            'net_salary' => $netSalary,
            'note' => $data['note'] ?? $payroll->note,
            'updated_by' => $userId,
        ]);

        $payroll->save();

        return $payroll->fresh(['user:id,name,email', 'creator:id,name']);
    }

    /**
     * 급여 삭제
     */
    public function destroy(int $id): bool
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        $payroll = Payroll::query()
            ->where('tenant_id', $tenantId)
            ->findOrFail($id);

        if (! $payroll->isDeletable()) {
            throw new BadRequestHttpException(__('error.payroll.not_deletable'));
        }

        $payroll->deleted_by = $userId;
        $payroll->save();
        $payroll->delete();

        return true;
    }

    // =========================================================================
    // 급여 확정/지급
    // =========================================================================

    /**
     * 급여 확정
     */
    public function confirm(int $id): Payroll
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        $payroll = Payroll::query()
            ->where('tenant_id', $tenantId)
            ->findOrFail($id);

        if (! $payroll->isConfirmable()) {
            throw new BadRequestHttpException(__('error.payroll.not_confirmable'));
        }

        $payroll->status = Payroll::STATUS_CONFIRMED;
        $payroll->confirmed_at = now();
        $payroll->confirmed_by = $userId;
        $payroll->updated_by = $userId;
        $payroll->save();

        return $payroll->fresh(['user:id,name,email', 'confirmer:id,name']);
    }

    /**
     * 급여 지급 처리
     */
    public function pay(int $id, ?int $withdrawalId = null): Payroll
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($id, $withdrawalId, $tenantId, $userId) {
            $payroll = Payroll::query()
                ->where('tenant_id', $tenantId)
                ->findOrFail($id);

            if (! $payroll->isPayable()) {
                throw new BadRequestHttpException(__('error.payroll.not_payable'));
            }

            // 출금 내역 연결 검증
            if ($withdrawalId) {
                $withdrawal = Withdrawal::query()
                    ->where('tenant_id', $tenantId)
                    ->where('id', $withdrawalId)
                    ->first();

                if (! $withdrawal) {
                    throw new BadRequestHttpException(__('error.payroll.invalid_withdrawal'));
                }
            }

            $payroll->status = Payroll::STATUS_PAID;
            $payroll->paid_at = now();
            $payroll->withdrawal_id = $withdrawalId;
            $payroll->updated_by = $userId;
            $payroll->save();

            return $payroll->fresh(['user:id,name,email', 'withdrawal']);
        });
    }

    /**
     * 일괄 확정
     */
    public function bulkConfirm(int $year, int $month): int
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return Payroll::query()
            ->where('tenant_id', $tenantId)
            ->where('pay_year', $year)
            ->where('pay_month', $month)
            ->where('status', Payroll::STATUS_DRAFT)
            ->update([
                'status' => Payroll::STATUS_CONFIRMED,
                'confirmed_at' => now(),
                'confirmed_by' => $userId,
                'updated_by' => $userId,
            ]);
    }

    // =========================================================================
    // 급여명세서
    // =========================================================================

    /**
     * 급여명세서 데이터
     */
    public function payslip(int $id): array
    {
        $payroll = $this->show($id);

        // 수당 목록
        $allowances = collect($payroll->allowances ?? [])->map(function ($item) {
            return [
                'name' => $item['name'] ?? '',
                'amount' => (float) ($item['amount'] ?? 0),
            ];
        })->toArray();

        // 공제 목록
        $deductions = collect($payroll->deductions ?? [])->map(function ($item) {
            return [
                'name' => $item['name'] ?? '',
                'amount' => (float) ($item['amount'] ?? 0),
            ];
        })->toArray();

        return [
            'payroll' => $payroll,
            'period' => $payroll->period_label,
            'employee' => [
                'id' => $payroll->user->id,
                'name' => $payroll->user->name,
                'email' => $payroll->user->email,
            ],
            'earnings' => [
                'base_salary' => (float) $payroll->base_salary,
                'overtime_pay' => (float) $payroll->overtime_pay,
                'bonus' => (float) $payroll->bonus,
                'allowances' => $allowances,
                'allowances_total' => (float) $payroll->allowances_total,
                'gross_total' => (float) $payroll->gross_salary,
            ],
            'deductions' => [
                'income_tax' => (float) $payroll->income_tax,
                'resident_tax' => (float) $payroll->resident_tax,
                'health_insurance' => (float) $payroll->health_insurance,
                'pension' => (float) $payroll->pension,
                'employment_insurance' => (float) $payroll->employment_insurance,
                'other_deductions' => $deductions,
                'other_total' => (float) $payroll->deductions_total,
                'total' => (float) $payroll->total_deductions,
            ],
            'net_salary' => (float) $payroll->net_salary,
            'status' => $payroll->status,
            'status_label' => $payroll->status_label,
            'paid_at' => $payroll->paid_at?->toIso8601String(),
        ];
    }

    // =========================================================================
    // 급여 일괄 계산
    // =========================================================================

    /**
     * 급여 일괄 계산 (생성 또는 업데이트)
     */
    public function calculate(int $year, int $month, ?array $userIds = null): Collection
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        // 급여 설정 가져오기
        $settings = PayrollSetting::getOrCreate($tenantId);

        // 대상 사용자 조회
        // TODO: 실제로는 직원 목록에서 급여 대상자를 조회해야 함
        // 여기서는 기존 급여 데이터만 업데이트

        return DB::transaction(function () use ($year, $month, $userIds, $tenantId, $userId, $settings) {
            $query = Payroll::query()
                ->where('tenant_id', $tenantId)
                ->where('pay_year', $year)
                ->where('pay_month', $month)
                ->where('status', Payroll::STATUS_DRAFT);

            if ($userIds) {
                $query->whereIn('user_id', $userIds);
            }

            $payrolls = $query->get();

            foreach ($payrolls as $payroll) {
                // 4대보험 재계산
                $baseSalary = (float) $payroll->base_salary;

                $healthInsurance = $settings->calculateHealthInsurance($baseSalary);
                $longTermCare = $settings->calculateLongTermCare($healthInsurance);
                $pension = $settings->calculatePension($baseSalary);
                $employmentInsurance = $settings->calculateEmploymentInsurance($baseSalary);

                // 건강보험에 장기요양보험 포함
                $totalHealthInsurance = $healthInsurance + $longTermCare;

                $payroll->health_insurance = $totalHealthInsurance;
                $payroll->pension = $pension;
                $payroll->employment_insurance = $employmentInsurance;

                // 주민세 재계산
                $payroll->resident_tax = $settings->calculateResidentTax($payroll->income_tax);

                // 총액 재계산
                $payroll->total_deductions = $payroll->calculateTotalDeductions();
                $payroll->net_salary = $payroll->calculateNetSalary();
                $payroll->updated_by = $userId;
                $payroll->save();
            }

            return $payrolls->fresh(['user:id,name,email']);
        });
    }

    // =========================================================================
    // 급여 설정
    // =========================================================================

    /**
     * 급여 설정 조회
     */
    public function getSettings(): PayrollSetting
    {
        $tenantId = $this->tenantId();

        return PayrollSetting::getOrCreate($tenantId);
    }

    /**
     * 급여 설정 수정
     */
    public function updateSettings(array $data): PayrollSetting
    {
        $tenantId = $this->tenantId();

        $settings = PayrollSetting::getOrCreate($tenantId);

        $settings->fill([
            'income_tax_rate' => $data['income_tax_rate'] ?? $settings->income_tax_rate,
            'resident_tax_rate' => $data['resident_tax_rate'] ?? $settings->resident_tax_rate,
            'health_insurance_rate' => $data['health_insurance_rate'] ?? $settings->health_insurance_rate,
            'long_term_care_rate' => $data['long_term_care_rate'] ?? $settings->long_term_care_rate,
            'pension_rate' => $data['pension_rate'] ?? $settings->pension_rate,
            'employment_insurance_rate' => $data['employment_insurance_rate'] ?? $settings->employment_insurance_rate,
            'pension_max_salary' => $data['pension_max_salary'] ?? $settings->pension_max_salary,
            'pension_min_salary' => $data['pension_min_salary'] ?? $settings->pension_min_salary,
            'pay_day' => $data['pay_day'] ?? $settings->pay_day,
            'auto_calculate' => $data['auto_calculate'] ?? $settings->auto_calculate,
            'allowance_types' => $data['allowance_types'] ?? $settings->allowance_types,
            'deduction_types' => $data['deduction_types'] ?? $settings->deduction_types,
        ]);

        $settings->save();

        return $settings;
    }

    // =========================================================================
    // 헬퍼 메서드
    // =========================================================================

    /**
     * 총지급액 계산
     */
    private function calculateGross(array $data): float
    {
        $baseSalary = (float) ($data['base_salary'] ?? 0);
        $overtimePay = (float) ($data['overtime_pay'] ?? 0);
        $bonus = (float) ($data['bonus'] ?? 0);

        $allowancesTotal = 0;
        if (! empty($data['allowances'])) {
            $allowancesTotal = collect($data['allowances'])->sum('amount');
        }

        return $baseSalary + $overtimePay + $bonus + $allowancesTotal;
    }

    /**
     * 총공제액 계산
     */
    private function calculateDeductions(array $data): float
    {
        $incomeTax = (float) ($data['income_tax'] ?? 0);
        $residentTax = (float) ($data['resident_tax'] ?? 0);
        $healthInsurance = (float) ($data['health_insurance'] ?? 0);
        $pension = (float) ($data['pension'] ?? 0);
        $employmentInsurance = (float) ($data['employment_insurance'] ?? 0);

        $deductionsTotal = 0;
        if (! empty($data['deductions'])) {
            $deductionsTotal = collect($data['deductions'])->sum('amount');
        }

        return $incomeTax + $residentTax + $healthInsurance + $pension + $employmentInsurance + $deductionsTotal;
    }
}
