<?php

namespace App\Http\Controllers\Api\Admin\HR;

use App\Http\Controllers\Controller;
use App\Models\Barobill\AccountCode;
use App\Models\Finance\JournalEntry;
use App\Models\Finance\JournalEntryLine;
use App\Models\Finance\TradingPartner;
use App\Models\HR\Payroll;
use App\Services\HR\PayrollService;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Symfony\Component\HttpFoundation\StreamedResponse;

class PayrollController extends Controller
{
    private const ALLOWED_PAYROLL_USERS = ['이경호', '전진선', '김보곤'];

    public function __construct(
        private PayrollService $payrollService
    ) {}

    private function checkPayrollAccess(): ?JsonResponse
    {
        if (! in_array(auth()->user()->name, self::ALLOWED_PAYROLL_USERS)) {
            return response()->json([
                'success' => false,
                'message' => '급여관리는 관계자만 볼 수 있습니다.',
            ], 403);
        }

        return null;
    }

    /**
     * 급여 목록 조회 (HTMX → HTML / 일반 → JSON)
     */
    public function index(Request $request): JsonResponse|Response
    {
        if ($denied = $this->checkPayrollAccess()) {
            return $denied;
        }

        $payrolls = $this->payrollService->getPayrolls(
            $request->all(),
            $request->integer('per_page', 20)
        );

        if ($request->header('HX-Request')) {
            return response(view('hr.payrolls.partials.table', compact('payrolls')));
        }

        return response()->json([
            'success' => true,
            'data' => $payrolls->items(),
            'meta' => [
                'current_page' => $payrolls->currentPage(),
                'last_page' => $payrolls->lastPage(),
                'per_page' => $payrolls->perPage(),
                'total' => $payrolls->total(),
            ],
        ]);
    }

    /**
     * 월간 통계 (HTMX → HTML / 일반 → JSON)
     */
    public function stats(Request $request): JsonResponse|Response
    {
        if ($denied = $this->checkPayrollAccess()) {
            return $denied;
        }

        $stats = $this->payrollService->getMonthlyStats(
            $request->integer('year') ?: null,
            $request->integer('month') ?: null
        );

        if ($request->header('HX-Request')) {
            return response(view('hr.payrolls.partials.stats', compact('stats')));
        }

        return response()->json([
            'success' => true,
            'data' => $stats,
        ]);
    }

    /**
     * 급여 등록
     */
    public function store(Request $request): JsonResponse
    {
        if ($denied = $this->checkPayrollAccess()) {
            return $denied;
        }

        $validated = $request->validate([
            'user_id' => 'required|integer|exists:users,id',
            'pay_year' => 'required|integer|min:2020|max:2100',
            'pay_month' => 'required|integer|min:1|max:12',
            'base_salary' => 'required|numeric|min:0',
            'overtime_pay' => 'nullable|numeric|min:0',
            'bonus' => 'nullable|numeric|min:0',
            'allowances' => 'nullable|array',
            'allowances.*.name' => 'required_with:allowances|string',
            'allowances.*.amount' => 'required_with:allowances|numeric|min:0',
            'deductions' => 'nullable|array',
            'deductions.*.name' => 'required_with:deductions|string',
            'deductions.*.amount' => 'required_with:deductions|numeric',
            'deduction_overrides' => 'nullable|array',
            'deduction_overrides.pension' => 'nullable|numeric',
            'deduction_overrides.health_insurance' => 'nullable|numeric',
            'deduction_overrides.long_term_care' => 'nullable|numeric',
            'deduction_overrides.employment_insurance' => 'nullable|numeric',
            'deduction_overrides.income_tax' => 'nullable|numeric',
            'deduction_overrides.resident_tax' => 'nullable|numeric',
            'note' => 'nullable|string|max:500',
        ]);

        try {
            $payroll = $this->payrollService->storePayroll($validated);

            return response()->json([
                'success' => true,
                'message' => '급여가 등록되었습니다.',
                'data' => $payroll,
            ], 201);
        } catch (\InvalidArgumentException $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
            ], 422);
        } catch (\Illuminate\Database\UniqueConstraintViolationException $e) {
            return response()->json([
                'success' => false,
                'message' => '해당 직원의 동일 기간 급여가 이미 등록되어 있습니다.',
            ], 422);
        } catch (\Throwable $e) {
            report($e);

            return response()->json([
                'success' => false,
                'message' => '급여 등록 중 오류가 발생했습니다.',
                'error' => config('app.debug') ? $e->getMessage() : null,
            ], 500);
        }
    }

    /**
     * 급여 수정
     */
    public function update(Request $request, int $id): JsonResponse
    {
        if ($denied = $this->checkPayrollAccess()) {
            return $denied;
        }

        $validated = $request->validate([
            'base_salary' => 'sometimes|required|numeric|min:0',
            'overtime_pay' => 'nullable|numeric|min:0',
            'bonus' => 'nullable|numeric|min:0',
            'allowances' => 'nullable|array',
            'allowances.*.name' => 'required_with:allowances|string',
            'allowances.*.amount' => 'required_with:allowances|numeric|min:0',
            'deductions' => 'nullable|array',
            'deductions.*.name' => 'required_with:deductions|string',
            'deductions.*.amount' => 'required_with:deductions|numeric',
            'deduction_overrides' => 'nullable|array',
            'deduction_overrides.pension' => 'nullable|numeric',
            'deduction_overrides.health_insurance' => 'nullable|numeric',
            'deduction_overrides.long_term_care' => 'nullable|numeric',
            'deduction_overrides.employment_insurance' => 'nullable|numeric',
            'deduction_overrides.income_tax' => 'nullable|numeric',
            'deduction_overrides.resident_tax' => 'nullable|numeric',
            'note' => 'nullable|string|max:500',
        ]);

        try {
            $payroll = $this->payrollService->updatePayroll($id, $validated);

            if (! $payroll) {
                return response()->json([
                    'success' => false,
                    'message' => '급여 정보를 찾을 수 없거나 수정할 수 없는 상태입니다.',
                ], 404);
            }

            return response()->json([
                'success' => true,
                'message' => '급여가 수정되었습니다.',
                'data' => $payroll,
            ]);
        } catch (\Throwable $e) {
            report($e);

            return response()->json([
                'success' => false,
                'message' => '급여 수정 중 오류가 발생했습니다.',
                'error' => config('app.debug') ? $e->getMessage() : null,
            ], 500);
        }
    }

    /**
     * 급여 삭제
     */
    public function destroy(Request $request, int $id): JsonResponse|Response
    {
        if ($denied = $this->checkPayrollAccess()) {
            return $denied;
        }

        try {
            $result = $this->payrollService->deletePayroll($id);

            if (! $result) {
                return response()->json([
                    'success' => false,
                    'message' => '급여 정보를 찾을 수 없거나 삭제할 수 없는 상태입니다.',
                ], 404);
            }

            if ($request->header('HX-Request')) {
                $payrolls = $this->payrollService->getPayrolls(
                    $request->all(),
                    $request->integer('per_page', 20)
                );

                return response(view('hr.payrolls.partials.table', compact('payrolls')));
            }

            return response()->json([
                'success' => true,
                'message' => '급여가 삭제되었습니다.',
            ]);
        } catch (\Throwable $e) {
            report($e);

            return response()->json([
                'success' => false,
                'message' => '급여 삭제 중 오류가 발생했습니다.',
                'error' => config('app.debug') ? $e->getMessage() : null,
            ], 500);
        }
    }

    /**
     * 급여 확정
     */
    public function confirm(Request $request, int $id): JsonResponse
    {
        if ($denied = $this->checkPayrollAccess()) {
            return $denied;
        }

        try {
            $payroll = $this->payrollService->confirmPayroll($id);

            if (! $payroll) {
                return response()->json([
                    'success' => false,
                    'message' => '급여를 확정할 수 없습니다.',
                ], 404);
            }

            return response()->json([
                'success' => true,
                'message' => '급여가 확정되었습니다.',
                'data' => $payroll,
            ]);
        } catch (\Throwable $e) {
            report($e);

            return response()->json([
                'success' => false,
                'message' => '급여 확정 중 오류가 발생했습니다.',
                'error' => config('app.debug') ? $e->getMessage() : null,
            ], 500);
        }
    }

    /**
     * 급여 확정 취소
     */
    public function unconfirm(Request $request, int $id): JsonResponse
    {
        if ($denied = $this->checkPayrollAccess()) {
            return $denied;
        }

        try {
            $payroll = $this->payrollService->unconfirmPayroll($id);

            if (! $payroll) {
                return response()->json([
                    'success' => false,
                    'message' => '급여 확정을 취소할 수 없습니다.',
                ], 404);
            }

            return response()->json([
                'success' => true,
                'message' => '급여 확정이 취소되었습니다.',
                'data' => $payroll,
            ]);
        } catch (\Throwable $e) {
            report($e);

            return response()->json([
                'success' => false,
                'message' => '급여 확정 취소 중 오류가 발생했습니다.',
                'error' => config('app.debug') ? $e->getMessage() : null,
            ], 500);
        }
    }

    /**
     * 급여 지급 처리
     */
    public function pay(Request $request, int $id): JsonResponse
    {
        if ($denied = $this->checkPayrollAccess()) {
            return $denied;
        }

        try {
            $payroll = $this->payrollService->payPayroll($id);

            if (! $payroll) {
                return response()->json([
                    'success' => false,
                    'message' => '급여를 지급 처리할 수 없습니다.',
                ], 404);
            }

            return response()->json([
                'success' => true,
                'message' => '급여가 지급 처리되었습니다.',
                'data' => $payroll,
            ]);
        } catch (\Throwable $e) {
            report($e);

            return response()->json([
                'success' => false,
                'message' => '급여 지급 처리 중 오류가 발생했습니다.',
                'error' => config('app.debug') ? $e->getMessage() : null,
            ], 500);
        }
    }

    /**
     * 전월 급여 복사 등록
     */
    public function copyFromPrevious(Request $request): JsonResponse
    {
        if ($denied = $this->checkPayrollAccess()) {
            return $denied;
        }

        $validated = $request->validate([
            'pay_year' => 'required|integer|min:2020|max:2100',
            'pay_month' => 'required|integer|min:1|max:12',
        ]);

        try {
            $result = $this->payrollService->copyFromPreviousMonth($validated['pay_year'], $validated['pay_month']);

            if (! empty($result['no_previous'])) {
                $prevYear = $validated['pay_month'] === 1 ? $validated['pay_year'] - 1 : $validated['pay_year'];
                $prevMonth = $validated['pay_month'] === 1 ? 12 : $validated['pay_month'] - 1;

                return response()->json([
                    'success' => false,
                    'message' => "{$prevYear}년 {$prevMonth}월 급여 데이터가 없습니다.",
                ], 422);
            }

            return response()->json([
                'success' => true,
                'message' => "전월 복사 완료: {$result['created']}건 생성, {$result['skipped']}건 건너뜀 (이미 존재).",
                'data' => $result,
            ]);
        } catch (\Throwable $e) {
            report($e);

            return response()->json([
                'success' => false,
                'message' => '전월 복사 중 오류가 발생했습니다.',
                'error' => config('app.debug') ? $e->getMessage() : null,
            ], 500);
        }
    }

    /**
     * 일괄 생성
     */
    public function bulkGenerate(Request $request): JsonResponse
    {
        if ($denied = $this->checkPayrollAccess()) {
            return $denied;
        }

        $validated = $request->validate([
            'year' => 'required|integer|min:2020|max:2100',
            'month' => 'required|integer|min:1|max:12',
        ]);

        try {
            $result = $this->payrollService->bulkGenerate($validated['year'], $validated['month']);

            return response()->json([
                'success' => true,
                'message' => "신규 {$result['created']}건 생성, {$result['skipped']}건 건너뜀 (이미 존재).",
                'data' => $result,
            ]);
        } catch (\Throwable $e) {
            report($e);

            return response()->json([
                'success' => false,
                'message' => '일괄 생성 중 오류가 발생했습니다.',
                'error' => config('app.debug') ? $e->getMessage() : null,
            ], 500);
        }
    }

    /**
     * 엑셀(XLSX) 내보내기 — 급여명세서 서식
     */
    public function export(Request $request): StreamedResponse|JsonResponse
    {
        if ($denied = $this->checkPayrollAccess()) {
            return $denied;
        }

        $payrolls = $this->payrollService->getExportData($request->all());
        $year = $request->input('year', now()->year);
        $month = $request->input('month', now()->month);

        $filename = "급여명세서_{$year}년{$month}월_".now()->format('Ymd').'.xlsx';

        $spreadsheet = new Spreadsheet;
        $sheet = $spreadsheet->getActiveSheet();
        $sheet->setTitle('급여명세서');

        $lastCol = 'O';
        $headers = ['No', '사원명', '부서', '기본급', "고정연장\n근로수당", "식대\n(비과세)", '총지급액', '국민연금', '건강보험', "장기요양\n보험", '고용보험', '근로소득세', '지방소득세', '총공제액', '실수령액'];

        // ── Row 1: 제목 ──
        $sheet->mergeCells("A1:{$lastCol}1");
        $sheet->setCellValue('A1', "< {$year}년도 {$month}월 급여명세서 >");
        $sheet->getStyle('A1')->applyFromArray([
            'font' => ['bold' => true, 'size' => 14],
            'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER],
        ]);
        $sheet->getRowDimension(1)->setRowHeight(30);

        // ── Row 2: 헤더 ──
        foreach ($headers as $colIdx => $header) {
            $cell = chr(65 + $colIdx).'2';
            $sheet->setCellValue($cell, $header);
        }
        $sheet->getStyle("A2:{$lastCol}2")->applyFromArray([
            'font' => ['bold' => true, 'size' => 10, 'color' => ['argb' => 'FFFFFFFF']],
            'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['argb' => 'FF1F3864']],
            'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER, 'wrapText' => true],
            'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN, 'color' => ['argb' => 'FF000000']]],
        ]);
        $sheet->getRowDimension(2)->setRowHeight(36);

        // ── Row 3~: 데이터 ──
        $dataStartRow = 3;
        $row = $dataStartRow;
        $moneyColumns = ['D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'];

        foreach ($payrolls as $idx => $payroll) {
            $profile = $payroll->user?->tenantProfiles?->first();
            $displayName = $profile?->display_name ?? $payroll->user?->name ?? '-';
            $department = $profile?->department?->name ?? '-';

            $sheet->setCellValue("A{$row}", $idx + 1);
            $sheet->setCellValue("B{$row}", $displayName);
            $sheet->setCellValue("C{$row}", $department);
            $sheet->setCellValue("D{$row}", (int) $payroll->base_salary);
            $sheet->setCellValue("E{$row}", (int) $payroll->overtime_pay);
            $sheet->setCellValue("F{$row}", (int) $payroll->bonus);
            $sheet->setCellValue("G{$row}", (int) $payroll->gross_salary);
            $sheet->setCellValue("H{$row}", (int) $payroll->pension);
            $sheet->setCellValue("I{$row}", (int) $payroll->health_insurance);
            $sheet->setCellValue("J{$row}", (int) $payroll->long_term_care);
            $sheet->setCellValue("K{$row}", (int) $payroll->employment_insurance);
            $sheet->setCellValue("L{$row}", (int) $payroll->income_tax);
            $sheet->setCellValue("M{$row}", (int) $payroll->resident_tax);
            $sheet->setCellValue("N{$row}", (int) $payroll->total_deductions);
            $sheet->setCellValue("O{$row}", (int) $payroll->net_salary);

            $row++;
        }

        // 빈 행 채움 (최소 10행까지 테두리)
        $minEndRow = $dataStartRow + 9;
        while ($row <= $minEndRow) {
            $row++;
        }
        $lastDataRow = $row - 1;

        // ── 데이터 영역 스타일 ──
        $sheet->getStyle("A{$dataStartRow}:{$lastCol}{$lastDataRow}")->applyFromArray([
            'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN, 'color' => ['argb' => 'FF000000']]],
            'alignment' => ['vertical' => Alignment::VERTICAL_CENTER],
            'font' => ['size' => 10],
        ]);

        // No 가운데 정렬
        $sheet->getStyle("A{$dataStartRow}:A{$lastDataRow}")
            ->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);

        // 금액 서식: #,##0 + 오른쪽 정렬
        foreach ($moneyColumns as $col) {
            $range = "{$col}{$dataStartRow}:{$col}{$lastDataRow}";
            $sheet->getStyle($range)->getNumberFormat()->setFormatCode('#,##0');
            $sheet->getStyle($range)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
        }

        // ── 합계 행 ──
        $sumRow = $lastDataRow + 1;
        $sheet->mergeCells("A{$sumRow}:C{$sumRow}");
        $sheet->setCellValue("A{$sumRow}", '합계');

        foreach ($moneyColumns as $col) {
            $sheet->setCellValue(
                "{$col}{$sumRow}",
                "=SUM({$col}{$dataStartRow}:{$col}{$lastDataRow})"
            );
        }

        $sheet->getStyle("A{$sumRow}:{$lastCol}{$sumRow}")->applyFromArray([
            'font' => ['bold' => true, 'size' => 10],
            'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['argb' => 'FFF2F2F2']],
            'borders' => ['allBorders' => ['borderStyle' => Border::BORDER_THIN, 'color' => ['argb' => 'FF000000']]],
            'alignment' => ['vertical' => Alignment::VERTICAL_CENTER],
        ]);
        $sheet->getStyle("A{$sumRow}:C{$sumRow}")
            ->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);

        foreach ($moneyColumns as $col) {
            $sheet->getStyle("{$col}{$sumRow}")->getNumberFormat()->setFormatCode('#,##0');
            $sheet->getStyle("{$col}{$sumRow}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
        }

        // ── 열 너비 ──
        $widths = ['A' => 6, 'B' => 12, 'C' => 12];
        foreach (range('D', 'O') as $col) {
            $widths[$col] = 13;
        }
        foreach ($widths as $col => $width) {
            $sheet->getColumnDimension($col)->setWidth($width);
        }

        // ── 응답 반환 ──
        return response()->streamDownload(function () use ($spreadsheet) {
            $writer = new Xlsx($spreadsheet);
            $writer->save('php://output');
            $spreadsheet->disconnectWorksheets();
        }, $filename, [
            'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            'Cache-Control' => 'max-age=0',
        ]);
    }

    /**
     * 급여 설정 조회 (HTMX → HTML / 일반 → JSON)
     */
    public function settingsIndex(Request $request): JsonResponse|Response
    {
        if ($denied = $this->checkPayrollAccess()) {
            return $denied;
        }

        $settings = $this->payrollService->getSettings();

        if ($request->header('HX-Request')) {
            return response(view('hr.payrolls.partials.settings', compact('settings')));
        }

        return response()->json([
            'success' => true,
            'data' => $settings,
        ]);
    }

    /**
     * 급여 설정 수정
     */
    public function settingsUpdate(Request $request): JsonResponse
    {
        if ($denied = $this->checkPayrollAccess()) {
            return $denied;
        }

        $validated = $request->validate([
            'health_insurance_rate' => 'nullable|numeric|min:0|max:100',
            'long_term_care_rate' => 'nullable|numeric|min:0|max:100',
            'pension_rate' => 'nullable|numeric|min:0|max:100',
            'employment_insurance_rate' => 'nullable|numeric|min:0|max:100',
            'pension_max_salary' => 'nullable|numeric|min:0',
            'pension_min_salary' => 'nullable|numeric|min:0',
            'pay_day' => 'nullable|integer|min:1|max:31',
            'auto_calculate' => 'nullable|boolean',
        ]);

        try {
            $settings = $this->payrollService->updateSettings($validated);

            return response()->json([
                'success' => true,
                'message' => '급여 설정이 저장되었습니다.',
                'data' => $settings,
            ]);
        } catch (\Throwable $e) {
            report($e);

            return response()->json([
                'success' => false,
                'message' => '설정 저장 중 오류가 발생했습니다.',
                'error' => config('app.debug') ? $e->getMessage() : null,
            ], 500);
        }
    }

    /**
     * 급여 일반전표 자동 생성
     */
    public function generateJournalEntry(Request $request): JsonResponse
    {
        if ($denied = $this->checkPayrollAccess()) {
            return $denied;
        }

        $request->validate([
            'year' => 'required|integer|min:2020|max:2100',
            'month' => 'required|integer|min:1|max:12',
        ]);

        $year = $request->integer('year');
        $month = $request->integer('month');
        $tenantId = session('selected_tenant_id', 1);
        $sourceKey = "payroll-{$year}-{$month}";

        // 중복 체크
        $existing = JournalEntry::forTenant($tenantId)
            ->where('source_type', 'payroll')
            ->where('source_key', $sourceKey)
            ->first();

        if ($existing) {
            return response()->json([
                'success' => false,
                'message' => "이미 {$month}월분 급여 전표가 존재합니다 ({$existing->entry_no})",
            ], 422);
        }

        // 해당월 급여 합산
        $sums = Payroll::forTenant($tenantId)
            ->forPeriod($year, $month)
            ->selectRaw('
                SUM(gross_salary) as total_gross,
                SUM(pension) as total_pension,
                SUM(health_insurance) as total_health,
                SUM(long_term_care) as total_ltc,
                SUM(employment_insurance) as total_emp,
                SUM(income_tax) as total_income_tax,
                SUM(resident_tax) as total_resident_tax,
                SUM(net_salary) as total_net
            ')
            ->first();

        if (! $sums || (int) $sums->total_gross === 0) {
            return response()->json([
                'success' => false,
                'message' => '해당 월 급여 데이터가 없습니다.',
            ], 422);
        }

        // 거래처 조회
        $partnerNames = ['임직원', '건강보험연금', '건강보험건강', '건강보험고용', '강서세무서', '강서구청'];
        $partners = TradingPartner::forTenant($tenantId)
            ->whereIn('name', $partnerNames)
            ->pluck('id', 'name');

        $missingPartners = array_diff($partnerNames, $partners->keys()->toArray());
        if (! empty($missingPartners)) {
            return response()->json([
                'success' => false,
                'message' => '거래처가 등록되어 있지 않습니다: '.implode(', ', $missingPartners),
            ], 422);
        }

        // 계정과목 조회
        $accountCodes = AccountCode::whereIn('code', ['801', '207', '205'])
            ->where('is_active', true)
            ->pluck('name', 'code');

        $missingCodes = array_diff(['801', '207', '205'], $accountCodes->keys()->toArray());
        if (! empty($missingCodes)) {
            return response()->json([
                'success' => false,
                'message' => '계정과목이 등록되어 있지 않습니다: '.implode(', ', $missingCodes),
            ], 422);
        }

        // 전표일자 (해당월 말일)
        $entryDate = Carbon::create($year, $month)->endOfMonth()->toDateString();
        $monthLabel = "{$month}월분";

        // 분개 행 구성
        $lines = [];
        $lineNo = 1;

        // 1. 차변: 801 급여 / 임직원
        $grossAmount = (int) $sums->total_gross;
        if ($grossAmount > 0) {
            $lines[] = [
                'dc_type' => 'debit',
                'account_code' => '801',
                'account_name' => $accountCodes['801'],
                'trading_partner_id' => $partners['임직원'],
                'trading_partner_name' => '임직원',
                'debit_amount' => $grossAmount,
                'credit_amount' => 0,
                'description' => "{$monthLabel} 급여",
                'line_no' => $lineNo++,
            ];
        }

        // 2. 대변: 207 예수금 / 건강보험연금 — 국민연금
        $pension = (int) $sums->total_pension;
        if ($pension > 0) {
            $lines[] = [
                'dc_type' => 'credit',
                'account_code' => '207',
                'account_name' => $accountCodes['207'],
                'trading_partner_id' => $partners['건강보험연금'],
                'trading_partner_name' => '건강보험연금',
                'debit_amount' => 0,
                'credit_amount' => $pension,
                'description' => '국민연금',
                'line_no' => $lineNo++,
            ];
        }

        // 3. 대변: 207 예수금 / 건강보험건강 — 건강보험
        $health = (int) $sums->total_health;
        if ($health > 0) {
            $lines[] = [
                'dc_type' => 'credit',
                'account_code' => '207',
                'account_name' => $accountCodes['207'],
                'trading_partner_id' => $partners['건강보험건강'],
                'trading_partner_name' => '건강보험건강',
                'debit_amount' => 0,
                'credit_amount' => $health,
                'description' => '건강보험',
                'line_no' => $lineNo++,
            ];
        }

        // 4. 대변: 207 예수금 / 건강보험건강 — 장기요양보험
        $ltc = (int) $sums->total_ltc;
        if ($ltc > 0) {
            $lines[] = [
                'dc_type' => 'credit',
                'account_code' => '207',
                'account_name' => $accountCodes['207'],
                'trading_partner_id' => $partners['건강보험건강'],
                'trading_partner_name' => '건강보험건강',
                'debit_amount' => 0,
                'credit_amount' => $ltc,
                'description' => '장기요양보험',
                'line_no' => $lineNo++,
            ];
        }

        // 5. 대변: 207 예수금 / 건강보험고용 — 고용보험
        $emp = (int) $sums->total_emp;
        if ($emp > 0) {
            $lines[] = [
                'dc_type' => 'credit',
                'account_code' => '207',
                'account_name' => $accountCodes['207'],
                'trading_partner_id' => $partners['건강보험고용'],
                'trading_partner_name' => '건강보험고용',
                'debit_amount' => 0,
                'credit_amount' => $emp,
                'description' => '고용보험',
                'line_no' => $lineNo++,
            ];
        }

        // 6. 대변: 207 예수금 / 강서세무서 — 근로소득세
        $incomeTax = (int) $sums->total_income_tax;
        if ($incomeTax > 0) {
            $lines[] = [
                'dc_type' => 'credit',
                'account_code' => '207',
                'account_name' => $accountCodes['207'],
                'trading_partner_id' => $partners['강서세무서'],
                'trading_partner_name' => '강서세무서',
                'debit_amount' => 0,
                'credit_amount' => $incomeTax,
                'description' => "{$monthLabel} 근로소득세",
                'line_no' => $lineNo++,
            ];
        }

        // 7. 대변: 207 예수금 / 강서구청 — 지방소득세
        $residentTax = (int) $sums->total_resident_tax;
        if ($residentTax > 0) {
            $lines[] = [
                'dc_type' => 'credit',
                'account_code' => '207',
                'account_name' => $accountCodes['207'],
                'trading_partner_id' => $partners['강서구청'],
                'trading_partner_name' => '강서구청',
                'debit_amount' => 0,
                'credit_amount' => $residentTax,
                'description' => "{$monthLabel} 지방소득세",
                'line_no' => $lineNo++,
            ];
        }

        // 8. 대변: 205 미지급비용 / 임직원 — 급여
        $netSalary = (int) $sums->total_net;
        if ($netSalary > 0) {
            $lines[] = [
                'dc_type' => 'credit',
                'account_code' => '205',
                'account_name' => $accountCodes['205'],
                'trading_partner_id' => $partners['임직원'],
                'trading_partner_name' => '임직원',
                'debit_amount' => 0,
                'credit_amount' => $netSalary,
                'description' => "{$monthLabel} 급여",
                'line_no' => $lineNo++,
            ];
        }

        // 차대 균형 검증
        $totalDebit = collect($lines)->sum('debit_amount');
        $totalCredit = collect($lines)->sum('credit_amount');

        if ($totalDebit !== $totalCredit || $totalDebit === 0) {
            return response()->json([
                'success' => false,
                'message' => "차변({$totalDebit})과 대변({$totalCredit})이 일치하지 않습니다.",
            ], 422);
        }

        try {
            $entry = DB::transaction(function () use ($tenantId, $entryDate, $totalDebit, $totalCredit, $sourceKey, $monthLabel, $lines) {
                $entryNo = JournalEntry::generateEntryNo($tenantId, $entryDate);

                $entry = JournalEntry::create([
                    'tenant_id' => $tenantId,
                    'entry_no' => $entryNo,
                    'entry_date' => $entryDate,
                    'entry_type' => 'general',
                    'description' => "{$monthLabel} 급여",
                    'total_debit' => $totalDebit,
                    'total_credit' => $totalCredit,
                    'status' => 'draft',
                    'source_type' => 'payroll',
                    'source_key' => $sourceKey,
                    'created_by_name' => auth()->user()?->name ?? '시스템',
                ]);

                foreach ($lines as $line) {
                    JournalEntryLine::create(array_merge($line, [
                        'tenant_id' => $tenantId,
                        'journal_entry_id' => $entry->id,
                    ]));
                }

                return $entry;
            });

            return response()->json([
                'success' => true,
                'message' => "급여 전표가 생성되었습니다 ({$entry->entry_no})",
                'data' => [
                    'entry_no' => $entry->entry_no,
                    'entry_date' => $entry->entry_date->toDateString(),
                ],
            ]);
        } catch (\Throwable $e) {
            report($e);

            return response()->json([
                'success' => false,
                'message' => '전표 생성 중 오류가 발생했습니다.',
                'error' => config('app.debug') ? $e->getMessage() : null,
            ], 500);
        }
    }

    /**
     * 급여 계산 미리보기 (AJAX)
     */
    public function calculate(Request $request): JsonResponse
    {
        if ($denied = $this->checkPayrollAccess()) {
            return $denied;
        }

        $validated = $request->validate([
            'base_salary' => 'required|numeric|min:0',
            'overtime_pay' => 'nullable|numeric|min:0',
            'bonus' => 'nullable|numeric|min:0',
            'allowances' => 'nullable|array',
            'deductions' => 'nullable|array',
            'user_id' => 'nullable|integer',
        ]);

        $familyCount = 1;
        if (! empty($validated['user_id'])) {
            $familyCount = $this->payrollService->resolveFamilyCount($validated['user_id']);
        }

        $result = $this->payrollService->calculateAmounts($validated, null, $familyCount);

        return response()->json([
            'success' => true,
            'data' => array_merge($result, ['family_count' => $familyCount]),
        ]);
    }
}
