<?php

namespace App\Http\Controllers\Finance;

use App\Http\Controllers\Controller;
use App\Models\Barobill\AccountCode;
use App\Models\Barobill\BankTransaction;
use App\Models\Barobill\CardTransaction;
use App\Models\Barobill\CardTransactionHide;
use App\Models\Finance\JournalEntry;
use App\Models\Finance\JournalEntryLine;
use App\Models\Finance\TradingPartner;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class JournalEntryController extends Controller
{
    /**
     * 전표 목록 조회
     */
    public function index(Request $request): JsonResponse
    {
        $tenantId = session('selected_tenant_id', 1);

        $query = JournalEntry::forTenant($tenantId)
            ->with('lines')
            ->orderByDesc('entry_date')
            ->orderByDesc('entry_no');

        if ($request->filled('start_date')) {
            $query->where('entry_date', '>=', $request->start_date);
        }
        if ($request->filled('end_date')) {
            $query->where('entry_date', '<=', $request->end_date);
        }
        if ($request->filled('status') && $request->status !== 'all') {
            $query->where('status', $request->status);
        }
        if ($request->filled('search')) {
            $search = $request->search;
            $query->where(function ($q) use ($search) {
                $q->where('entry_no', 'like', "%{$search}%")
                    ->orWhere('description', 'like', "%{$search}%");
            });
        }

        $entries = $query->get();

        $data = $entries->map(function ($entry) {
            return [
                'id' => $entry->id,
                'entry_no' => $entry->entry_no,
                'entry_date' => $entry->entry_date->format('Y-m-d'),
                'description' => $entry->description,
                'total_debit' => $entry->total_debit,
                'total_credit' => $entry->total_credit,
                'status' => $entry->status,
                'source_type' => $entry->source_type,
                'created_by_name' => $entry->created_by_name,
                'lines' => $entry->lines->map(function ($line) {
                    return [
                        'id' => $line->id,
                        'line_no' => $line->line_no,
                        'dc_type' => $line->dc_type,
                        'account_code' => $line->account_code,
                        'account_name' => $line->account_name,
                        'trading_partner_id' => $line->trading_partner_id,
                        'trading_partner_name' => $line->trading_partner_name,
                        'debit_amount' => $line->debit_amount,
                        'credit_amount' => $line->credit_amount,
                        'description' => $line->description,
                    ];
                }),
            ];
        });

        $stats = [
            'totalCount' => $entries->count(),
            'totalDebit' => $entries->sum('total_debit'),
            'totalCredit' => $entries->sum('total_credit'),
            'draftCount' => $entries->where('status', 'draft')->count(),
            'confirmedCount' => $entries->where('status', 'confirmed')->count(),
        ];

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

    /**
     * 전표 상세 조회
     */
    public function show(int $id): JsonResponse
    {
        $tenantId = session('selected_tenant_id', 1);

        $entry = JournalEntry::forTenant($tenantId)
            ->with('lines')
            ->findOrFail($id);

        return response()->json([
            'success' => true,
            'data' => [
                'id' => $entry->id,
                'entry_no' => $entry->entry_no,
                'entry_date' => $entry->entry_date->format('Y-m-d'),
                'entry_type' => $entry->entry_type,
                'description' => $entry->description,
                'total_debit' => $entry->total_debit,
                'total_credit' => $entry->total_credit,
                'status' => $entry->status,
                'created_by_name' => $entry->created_by_name,
                'attachment_note' => $entry->attachment_note,
                'lines' => $entry->lines->map(function ($line) {
                    return [
                        'id' => $line->id,
                        'line_no' => $line->line_no,
                        'dc_type' => $line->dc_type,
                        'account_code' => $line->account_code,
                        'account_name' => $line->account_name,
                        'trading_partner_id' => $line->trading_partner_id,
                        'trading_partner_name' => $line->trading_partner_name,
                        'debit_amount' => $line->debit_amount,
                        'credit_amount' => $line->credit_amount,
                        'description' => $line->description,
                    ];
                }),
            ],
        ]);
    }

    /**
     * 전표 저장
     */
    public function store(Request $request): JsonResponse
    {
        $request->validate([
            'entry_date' => 'required|date',
            'description' => 'nullable|string|max:500',
            'attachment_note' => 'nullable|string',
            'lines' => 'required|array|min:2',
            'lines.*.dc_type' => 'required|in:debit,credit',
            'lines.*.account_code' => 'required|string|max:10',
            'lines.*.account_name' => 'required|string|max:100',
            'lines.*.trading_partner_id' => 'nullable|integer',
            'lines.*.trading_partner_name' => 'nullable|string|max:100',
            'lines.*.debit_amount' => 'required|integer|min:0',
            'lines.*.credit_amount' => 'required|integer|min:0',
            'lines.*.description' => 'nullable|string|max:300',
        ]);

        $tenantId = session('selected_tenant_id', 1);
        $lines = $request->lines;

        $totalDebit = collect($lines)->sum('debit_amount');
        $totalCredit = collect($lines)->sum('credit_amount');

        if ($totalDebit !== $totalCredit || $totalDebit === 0) {
            return response()->json([
                'success' => false,
                'message' => '차변합계와 대변합계가 일치해야 하며 0보다 커야 합니다.',
            ], 422);
        }

        $maxRetries = 3;
        $lastError = null;

        for ($attempt = 0; $attempt < $maxRetries; $attempt++) {
            try {
                $entry = DB::transaction(function () use ($tenantId, $request, $lines, $totalDebit, $totalCredit) {
                    $entryNo = JournalEntry::generateEntryNo($tenantId, $request->entry_date);

                    $entry = JournalEntry::create([
                        'tenant_id' => $tenantId,
                        'entry_no' => $entryNo,
                        'entry_date' => $request->entry_date,
                        'description' => $request->description,
                        'total_debit' => $totalDebit,
                        'total_credit' => $totalCredit,
                        'status' => 'draft',
                        'created_by_name' => auth()->user()?->name ?? '시스템',
                        'attachment_note' => $request->attachment_note,
                    ]);

                    foreach ($lines as $i => $line) {
                        JournalEntryLine::create([
                            'tenant_id' => $tenantId,
                            'journal_entry_id' => $entry->id,
                            'line_no' => $i + 1,
                            'dc_type' => $line['dc_type'],
                            'account_code' => $line['account_code'],
                            'account_name' => $line['account_name'],
                            'trading_partner_id' => $line['trading_partner_id'] ?? null,
                            'trading_partner_name' => $line['trading_partner_name'] ?? null,
                            'debit_amount' => $line['debit_amount'],
                            'credit_amount' => $line['credit_amount'],
                            'description' => $line['description'] ?? null,
                        ]);
                    }

                    return $entry;
                });

                return response()->json([
                    'success' => true,
                    'message' => '전표가 저장되었습니다.',
                    'data' => ['id' => $entry->id, 'entry_no' => $entry->entry_no],
                ]);
            } catch (\Illuminate\Database\QueryException $e) {
                $lastError = $e;
                if ($e->errorInfo[1] === 1062) {
                    continue;
                }
                break;
            } catch (\Throwable $e) {
                $lastError = $e;
                break;
            }
        }

        Log::error('전표 저장 실패', ['error' => $lastError->getMessage()]);

        return response()->json([
            'success' => false,
            'message' => '전표 저장 실패: '.$lastError->getMessage(),
        ], 500);
    }

    /**
     * 전표 수정
     */
    public function update(Request $request, int $id): JsonResponse
    {
        $request->validate([
            'entry_date' => 'required|date',
            'description' => 'nullable|string|max:500',
            'attachment_note' => 'nullable|string',
            'lines' => 'required|array|min:2',
            'lines.*.dc_type' => 'required|in:debit,credit',
            'lines.*.account_code' => 'required|string|max:10',
            'lines.*.account_name' => 'required|string|max:100',
            'lines.*.trading_partner_id' => 'nullable|integer',
            'lines.*.trading_partner_name' => 'nullable|string|max:100',
            'lines.*.debit_amount' => 'required|integer|min:0',
            'lines.*.credit_amount' => 'required|integer|min:0',
            'lines.*.description' => 'nullable|string|max:300',
        ]);

        $tenantId = session('selected_tenant_id', 1);
        $lines = $request->lines;

        $totalDebit = collect($lines)->sum('debit_amount');
        $totalCredit = collect($lines)->sum('credit_amount');

        if ($totalDebit !== $totalCredit || $totalDebit === 0) {
            return response()->json([
                'success' => false,
                'message' => '차변합계와 대변합계가 일치해야 하며 0보다 커야 합니다.',
            ], 422);
        }

        DB::transaction(function () use ($tenantId, $id, $request, $lines, $totalDebit, $totalCredit) {
            $entry = JournalEntry::forTenant($tenantId)->findOrFail($id);

            $entry->update([
                'entry_date' => $request->entry_date,
                'description' => $request->description,
                'total_debit' => $totalDebit,
                'total_credit' => $totalCredit,
                'attachment_note' => $request->attachment_note,
            ]);

            // 기존 lines 삭제 후 재생성
            $entry->lines()->delete();

            foreach ($lines as $i => $line) {
                JournalEntryLine::create([
                    'tenant_id' => $tenantId,
                    'journal_entry_id' => $entry->id,
                    'line_no' => $i + 1,
                    'dc_type' => $line['dc_type'],
                    'account_code' => $line['account_code'],
                    'account_name' => $line['account_name'],
                    'trading_partner_id' => $line['trading_partner_id'] ?? null,
                    'trading_partner_name' => $line['trading_partner_name'] ?? null,
                    'debit_amount' => $line['debit_amount'],
                    'credit_amount' => $line['credit_amount'],
                    'description' => $line['description'] ?? null,
                ]);
            }
        });

        return response()->json([
            'success' => true,
            'message' => '전표가 수정되었습니다.',
        ]);
    }

    /**
     * 전표 삭제 (soft delete)
     */
    public function destroy(int $id): JsonResponse
    {
        $tenantId = session('selected_tenant_id', 1);
        $entry = JournalEntry::forTenant($tenantId)->findOrFail($id);
        $entry->delete();

        return response()->json([
            'success' => true,
            'message' => '전표가 삭제되었습니다.',
        ]);
    }

    /**
     * 다음 전표번호 미리보기
     */
    public function nextEntryNo(Request $request): JsonResponse
    {
        $tenantId = session('selected_tenant_id', 1);
        $date = $request->get('date', date('Y-m-d'));
        $entryNo = JournalEntry::generateEntryNo($tenantId, $date);

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

    /**
     * 계정과목 목록
     */
    public function accountCodes(): JsonResponse
    {
        $codes = AccountCode::getActive();

        return response()->json([
            'success' => true,
            'data' => $codes->map(function ($code) {
                return [
                    'code' => $code->code,
                    'name' => $code->name,
                    'category' => $code->category,
                ];
            }),
        ]);
    }

    /**
     * 거래처 목록
     */
    public function tradingPartners(Request $request): JsonResponse
    {
        $tenantId = session('selected_tenant_id', 1);

        $query = TradingPartner::forTenant($tenantId)->active();

        if ($request->filled('search')) {
            $search = $request->search;
            $query->where(function ($q) use ($search) {
                $q->where('name', 'like', "%{$search}%")
                    ->orWhere('biz_no', 'like', "%{$search}%");
            });
        }

        $partners = $query->orderBy('name')->limit(50)->get();

        return response()->json([
            'success' => true,
            'data' => $partners->map(function ($p) {
                return [
                    'id' => $p->id,
                    'name' => $p->name,
                    'biz_no' => $p->biz_no,
                    'type' => $p->type,
                    'category' => $p->category,
                    'ceo' => $p->ceo,
                    'address' => $p->address,
                    'email' => $p->email,
                ];
            }),
        ]);
    }

    // ================================================================
    // 은행거래 기반 분개 API
    // ================================================================

    /**
     * 은행거래 목록 조회 (DB 직접 조회 + 분개상태 병합)
     */
    public function bankTransactions(Request $request): JsonResponse
    {
        try {
            $tenantId = session('selected_tenant_id', 1);
            $startDate = $request->input('startDate', date('Ymd'));
            $endDate = $request->input('endDate', date('Ymd'));
            $accountNum = $request->input('accountNum', '');

            // barobill_bank_transactions 테이블에서 직접 조회 (중복 제거)
            $normalizedAccNum = ! empty($accountNum) ? str_replace('-', '', $accountNum) : null;

            $dedupQuery = BankTransaction::where('tenant_id', $tenantId)
                ->whereBetween('trans_date', [$startDate, $endDate]);

            if ($normalizedAccNum) {
                $dedupQuery->where('bank_account_num', $normalizedAccNum);
            }

            $latestIds = $dedupQuery
                ->selectRaw('MAX(id) as id')
                ->groupBy('bank_account_num', 'trans_dt', 'deposit', 'withdraw', 'balance', 'summary')
                ->pluck('id');

            $transactions = BankTransaction::whereIn('id', $latestIds)
                ->orderBy('trans_date')
                ->orderBy('trans_time')
                ->get();

            // 계좌별 이전 기간 잔액 조회 후 running balance 재계산
            $prevBalances = $this->getPreviousBalances($tenantId, $startDate, $normalizedAccNum);

            // 로그 데이터 변환 (시간순 ASC로 running balance 계산)
            $logs = [];
            $accountBalances = $prevBalances; // 계좌별 현재 잔액 추적
            foreach ($transactions as $tx) {
                $accNum = $tx->bank_account_num;
                $deposit = (int) $tx->deposit;
                $withdraw = (int) $tx->withdraw;

                // running balance 계산
                $prevBal = $accountBalances[$accNum] ?? 0;
                $newBal = $prevBal + $deposit - $withdraw;
                $accountBalances[$accNum] = $newBal;

                $logs[] = [
                    'uniqueKey' => $tx->unique_key,
                    'transDate' => $tx->trans_date,
                    'transTime' => $tx->trans_time,
                    'bankAccountNum' => $accNum,
                    'bankName' => $tx->bank_name,
                    'deposit' => $deposit,
                    'withdraw' => $withdraw,
                    'balance' => $newBal,
                    'summary' => $tx->summary,
                    'cast' => $tx->cast,
                    'memo' => $tx->memo,
                    'transOffice' => $tx->trans_office,
                    'accountCode' => $tx->account_code,
                    'accountName' => $tx->account_name,
                    'isManual' => $tx->is_manual,
                ];
            }
            // 최신순 정렬 (DESC)
            $logs = array_reverse($logs);

            // 각 거래의 uniqueKey 수집
            $uniqueKeys = array_column($logs, 'uniqueKey');

            // 분개 완료된 source_key 조회
            $journaledKeys = JournalEntry::getJournaledSourceKeys($tenantId, 'bank_transaction', $uniqueKeys);
            $journaledKeysMap = array_flip($journaledKeys);

            // 분개된 전표 ID도 조회 (레거시 키 포함)
            $journalMap = [];
            if (! empty($journaledKeys)) {
                // 새 키 + 레거시 키 모두 검색
                $allSearchKeys = $journaledKeys;
                $legacyMap = [];
                foreach ($journaledKeys as $key) {
                    $parts = explode('|', $key);
                    if (count($parts) === 6) {
                        $legacyKey = implode('|', array_slice($parts, 0, 5));
                        $allSearchKeys[] = $legacyKey;
                        $legacyMap[$legacyKey] = $key;
                    }
                }

                $journals = JournalEntry::where('tenant_id', $tenantId)
                    ->where('source_type', 'bank_transaction')
                    ->whereIn('source_key', array_unique($allSearchKeys))
                    ->select('id', 'source_key', 'entry_no')
                    ->get();
                foreach ($journals as $j) {
                    $mappedKey = $legacyMap[$j->source_key] ?? $j->source_key;
                    $journalMap[$mappedKey] = ['id' => $j->id, 'entry_no' => $j->entry_no];
                }
            }

            // 각 거래에 분개 상태 추가
            foreach ($logs as &$log) {
                $key = $log['uniqueKey'] ?? '';
                $log['hasJournal'] = isset($journaledKeysMap[$key]);
                $log['journalId'] = $journalMap[$key]['id'] ?? null;
                $log['journalEntryNo'] = $journalMap[$key]['entry_no'] ?? null;
            }
            unset($log);

            // 통계
            $totalCount = count($logs);
            $depositSum = array_sum(array_column($logs, 'deposit'));
            $withdrawSum = array_sum(array_column($logs, 'withdraw'));
            $journaledCount = count($journaledKeys);

            // 계좌 목록 (드롭다운용)
            $accounts = BankTransaction::where('tenant_id', $tenantId)
                ->select('bank_account_num', 'bank_name')
                ->distinct()
                ->get()
                ->toArray();

            return response()->json([
                'success' => true,
                'data' => [
                    'logs' => $logs,
                    'accounts' => $accounts,
                    'summary' => [
                        'totalCount' => $totalCount,
                        'depositSum' => $depositSum,
                        'withdrawSum' => $withdrawSum,
                    ],
                    'journalStats' => [
                        'journaledCount' => $journaledCount,
                        'unjournaledCount' => $totalCount - $journaledCount,
                    ],
                ],
            ]);
        } catch (\Throwable $e) {
            Log::error('은행거래 목록 조회 오류: '.$e->getMessage());

            return response()->json([
                'success' => false,
                'message' => '은행거래 목록 조회 실패: '.$e->getMessage(),
            ], 500);
        }
    }

    /**
     * 은행거래 기반 전표 생성
     */
    public function storeFromBank(Request $request): JsonResponse
    {
        $request->validate([
            'source_key' => 'required|string|max:255',
            'entry_date' => 'required|date',
            'description' => 'nullable|string|max:500',
            'lines' => 'required|array|min:2',
            'lines.*.dc_type' => 'required|in:debit,credit',
            'lines.*.account_code' => 'required|string|max:10',
            'lines.*.account_name' => 'required|string|max:100',
            'lines.*.trading_partner_id' => 'nullable|integer',
            'lines.*.trading_partner_name' => 'nullable|string|max:100',
            'lines.*.debit_amount' => 'required|integer|min:0',
            'lines.*.credit_amount' => 'required|integer|min:0',
            'lines.*.description' => 'nullable|string|max:300',
        ]);

        $tenantId = session('selected_tenant_id', 1);
        $lines = $request->lines;

        $totalDebit = collect($lines)->sum('debit_amount');
        $totalCredit = collect($lines)->sum('credit_amount');

        if ($totalDebit !== $totalCredit || $totalDebit === 0) {
            return response()->json([
                'success' => false,
                'message' => '차변합계와 대변합계가 일치해야 하며 0보다 커야 합니다.',
            ], 422);
        }

        // 중복 분개 체크
        $existing = JournalEntry::getJournalBySourceKey($tenantId, 'bank_transaction', $request->source_key);
        if ($existing) {
            return response()->json([
                'success' => false,
                'message' => '이미 분개가 완료된 거래입니다. (전표번호: '.$existing->entry_no.')',
            ], 422);
        }

        $maxRetries = 3;
        $lastError = null;

        for ($attempt = 0; $attempt < $maxRetries; $attempt++) {
            try {
                $entry = DB::transaction(function () use ($tenantId, $request, $lines, $totalDebit, $totalCredit) {
                    $entryNo = JournalEntry::generateEntryNo($tenantId, $request->entry_date);

                    $entry = JournalEntry::create([
                        'tenant_id' => $tenantId,
                        'entry_no' => $entryNo,
                        'entry_date' => $request->entry_date,
                        'description' => $request->description,
                        'total_debit' => $totalDebit,
                        'total_credit' => $totalCredit,
                        'status' => 'draft',
                        'source_type' => 'bank_transaction',
                        'source_key' => $request->source_key,
                        'created_by_name' => auth()->user()?->name ?? '시스템',
                    ]);

                    foreach ($lines as $i => $line) {
                        JournalEntryLine::create([
                            'tenant_id' => $tenantId,
                            'journal_entry_id' => $entry->id,
                            'line_no' => $i + 1,
                            'dc_type' => $line['dc_type'],
                            'account_code' => $line['account_code'],
                            'account_name' => $line['account_name'],
                            'trading_partner_id' => $line['trading_partner_id'] ?? null,
                            'trading_partner_name' => $line['trading_partner_name'] ?? null,
                            'debit_amount' => $line['debit_amount'],
                            'credit_amount' => $line['credit_amount'],
                            'description' => $line['description'] ?? null,
                        ]);
                    }

                    return $entry;
                });

                return response()->json([
                    'success' => true,
                    'message' => '분개가 저장되었습니다.',
                    'data' => ['id' => $entry->id, 'entry_no' => $entry->entry_no],
                ]);
            } catch (\Illuminate\Database\QueryException $e) {
                $lastError = $e;
                if ($e->errorInfo[1] === 1062) {
                    continue;
                }
                break;
            } catch (\Throwable $e) {
                $lastError = $e;
                break;
            }
        }

        Log::error('은행거래 분개 저장 오류: '.$lastError->getMessage());

        return response()->json([
            'success' => false,
            'message' => '분개 저장 실패: '.$lastError->getMessage(),
        ], 500);
    }

    /**
     * 특정 은행거래의 기존 분개 조회
     */
    public function bankJournals(Request $request): JsonResponse
    {
        $tenantId = session('selected_tenant_id', 1);
        $sourceKey = $request->get('source_key');

        if (! $sourceKey) {
            return response()->json(['success' => false, 'message' => 'source_key가 필요합니다.'], 422);
        }

        // 레거시(summary 미포함) 형식과 신규(summary 포함) 형식 모두 매칭
        $keys = [$sourceKey];
        $parts = explode('|', $sourceKey);
        if (count($parts) === 6) {
            $keys[] = implode('|', array_slice($parts, 0, 5));
        }

        $entry = JournalEntry::forTenant($tenantId)
            ->where('source_type', 'bank_transaction')
            ->whereIn('source_key', $keys)
            ->with('lines')
            ->first();

        if (! $entry) {
            return response()->json(['success' => true, 'data' => null]);
        }

        return response()->json([
            'success' => true,
            'data' => [
                'id' => $entry->id,
                'entry_no' => $entry->entry_no,
                'entry_date' => $entry->entry_date->format('Y-m-d'),
                'description' => $entry->description,
                'total_debit' => $entry->total_debit,
                'total_credit' => $entry->total_credit,
                'status' => $entry->status,
                'lines' => $entry->lines->map(function ($line) {
                    return [
                        'id' => $line->id,
                        'line_no' => $line->line_no,
                        'dc_type' => $line->dc_type,
                        'account_code' => $line->account_code,
                        'account_name' => $line->account_name,
                        'trading_partner_id' => $line->trading_partner_id,
                        'trading_partner_name' => $line->trading_partner_name,
                        'debit_amount' => $line->debit_amount,
                        'credit_amount' => $line->credit_amount,
                        'description' => $line->description,
                    ];
                }),
            ],
        ]);
    }

    /**
     * 은행거래 분개 삭제 (soft delete)
     */
    public function deleteBankJournal(int $id): JsonResponse
    {
        $tenantId = session('selected_tenant_id', 1);

        $entry = JournalEntry::forTenant($tenantId)
            ->where('source_type', 'bank_transaction')
            ->findOrFail($id);

        $entry->delete();

        return response()->json([
            'success' => true,
            'message' => '분개가 삭제되었습니다.',
        ]);
    }

    /**
     * 조회기간 이전의 계좌별 잔액 계산
     * EaccountController::findBaseBalance() 패턴을 계좌별로 확장
     */
    private function getPreviousBalances(int $tenantId, string $startDate, ?string $accountNum): array
    {
        $query = BankTransaction::where('tenant_id', $tenantId)
            ->where('trans_date', '<', $startDate);

        if ($accountNum) {
            $query->where('bank_account_num', $accountNum);
        }

        // 중복 제거 (bankTransactions와 동일 기준)
        $latestIds = (clone $query)
            ->selectRaw('MAX(id) as id')
            ->groupBy('bank_account_num', 'trans_dt', 'deposit', 'withdraw')
            ->pluck('id');

        if ($latestIds->isEmpty()) {
            return [];
        }

        $transactions = BankTransaction::whereIn('id', $latestIds)
            ->orderBy('trans_date')
            ->orderBy('trans_time')
            ->get();

        $balances = [];
        foreach ($transactions as $tx) {
            $accNum = $tx->bank_account_num;
            if (! $tx->is_manual && (float) $tx->balance != 0) {
                // API 데이터: 바로빌이 제공한 잔액을 앵커로 사용
                $balances[$accNum] = (float) $tx->balance;
            } else {
                // 수동입력 또는 잔액 0: 이전 잔액에서 입출금 계산
                $prev = $balances[$accNum] ?? 0;
                $balances[$accNum] = $prev + (float) $tx->deposit - (float) $tx->withdraw;
            }
        }

        return $balances;
    }

    /**
     * 계정과목 전체 목록 (활성/비활성 포함)
     */
    public function accountCodesAll(): JsonResponse
    {
        $codes = AccountCode::getAll();

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

    /**
     * 계정과목 추가
     */
    public function accountCodeStore(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'code' => 'required|string|max:10',
            'name' => 'required|string|max:100',
            'category' => 'nullable|string|max:50',
        ]);

        if (AccountCode::where('code', $validated['code'])->exists()) {
            return response()->json([
                'success' => false,
                'error' => '이미 존재하는 계정과목 코드입니다.',
            ], 422);
        }

        $maxSort = AccountCode::max('sort_order') ?? 0;

        $accountCode = AccountCode::create([
            'tenant_id' => 1,
            'code' => $validated['code'],
            'name' => $validated['name'],
            'category' => $validated['category'] ?? null,
            'sort_order' => $maxSort + 1,
            'is_active' => true,
        ]);

        return response()->json([
            'success' => true,
            'message' => '계정과목이 추가되었습니다.',
            'data' => $accountCode,
        ]);
    }

    /**
     * 계정과목 수정
     */
    public function accountCodeUpdate(Request $request, int $id): JsonResponse
    {
        $accountCode = AccountCode::find($id);
        if (! $accountCode) {
            return response()->json(['success' => false, 'error' => '계정과목을 찾을 수 없습니다.'], 404);
        }

        $validated = $request->validate([
            'code' => 'sometimes|string|max:10',
            'name' => 'sometimes|string|max:100',
            'category' => 'nullable|string|max:50',
            'is_active' => 'sometimes|boolean',
        ]);

        if (isset($validated['code']) && $validated['code'] !== $accountCode->code) {
            if (AccountCode::where('code', $validated['code'])->where('id', '!=', $id)->exists()) {
                return response()->json(['success' => false, 'error' => '이미 존재하는 계정과목 코드입니다.'], 422);
            }
        }

        $accountCode->update($validated);

        return response()->json([
            'success' => true,
            'message' => '계정과목이 수정되었습니다.',
            'data' => $accountCode,
        ]);
    }

    /**
     * 계정과목 삭제
     */
    public function accountCodeDestroy(int $id): JsonResponse
    {
        $accountCode = AccountCode::find($id);
        if (! $accountCode) {
            return response()->json(['success' => false, 'error' => '계정과목을 찾을 수 없습니다.'], 404);
        }

        $accountCode->delete();

        return response()->json([
            'success' => true,
            'message' => '계정과목이 삭제되었습니다.',
        ]);
    }

    // ================================================================
    // 카드거래 기반 분개 API
    // ================================================================

    /**
     * 카드거래 목록 조회 (DB 직접 조회 + 분개상태 병합)
     */
    public function cardTransactions(Request $request): JsonResponse
    {
        try {
            $tenantId = session('selected_tenant_id', 1);
            $startDate = $request->input('startDate', date('Ymd'));
            $endDate = $request->input('endDate', date('Ymd'));
            $cardNum = $request->input('cardNum', '');

            $query = CardTransaction::where('tenant_id', $tenantId)
                ->whereBetween('use_date', [$startDate, $endDate]);

            if (! empty($cardNum)) {
                $query->where('card_num', $cardNum);
            }

            $transactions = $query->orderBy('use_date', 'desc')
                ->orderBy('use_time', 'desc')
                ->get();

            // 숨김 처리된 거래 제외
            $hiddenKeys = CardTransactionHide::getHiddenKeys($tenantId, $startDate, $endDate);
            $hiddenKeysMap = $hiddenKeys->flip();

            $transactions = $transactions->filter(function ($tx) use ($hiddenKeysMap) {
                return ! $hiddenKeysMap->has($tx->unique_key);
            });

            // 로그 데이터 변환
            $logs = [];
            foreach ($transactions as $tx) {
                $supplyAmount = $tx->modified_supply_amount !== null
                    ? (int) $tx->modified_supply_amount
                    : (int) $tx->approval_amount - (int) $tx->tax;
                $taxAmount = $tx->modified_tax !== null
                    ? (int) $tx->modified_tax
                    : (int) $tx->tax;

                $logs[] = [
                    'uniqueKey' => $tx->unique_key,
                    'useDate' => $tx->use_date,
                    'useTime' => $tx->use_time,
                    'cardNum' => $tx->card_num,
                    'cardCompanyName' => $tx->card_company_name,
                    'approvalNum' => $tx->approval_num,
                    'approvalType' => $tx->approval_type,
                    'approvalAmount' => (int) $tx->approval_amount,
                    'supplyAmount' => $supplyAmount,
                    'taxAmount' => $taxAmount,
                    'merchantName' => $tx->merchant_name,
                    'merchantBizNum' => $tx->merchant_biz_num,
                    'deductionType' => $tx->deduction_type,
                    'accountCode' => $tx->account_code,
                    'accountName' => $tx->account_name,
                    'memo' => $tx->memo,
                    'description' => $tx->description,
                ];
            }

            // 각 거래의 uniqueKey 수집
            $uniqueKeys = array_column($logs, 'uniqueKey');

            // 분개 완료된 source_key 조회
            $journaledKeys = JournalEntry::getJournaledSourceKeys($tenantId, 'ecard_transaction', $uniqueKeys);
            $journaledKeysMap = array_flip($journaledKeys);

            // 분개된 전표 ID 조회
            $journalMap = [];
            if (! empty($journaledKeys)) {
                $journals = JournalEntry::where('tenant_id', $tenantId)
                    ->where('source_type', 'ecard_transaction')
                    ->whereIn('source_key', $journaledKeys)
                    ->select('id', 'source_key', 'entry_no')
                    ->get();
                foreach ($journals as $j) {
                    $journalMap[$j->source_key] = ['id' => $j->id, 'entry_no' => $j->entry_no];
                }
            }

            // 각 거래에 분개 상태 추가
            foreach ($logs as &$log) {
                $key = $log['uniqueKey'] ?? '';
                $log['hasJournal'] = isset($journaledKeysMap[$key]);
                $log['journalId'] = $journalMap[$key]['id'] ?? null;
                $log['journalEntryNo'] = $journalMap[$key]['entry_no'] ?? null;
            }
            unset($log);

            // 통계
            $totalCount = count($logs);
            $totalAmount = array_sum(array_column($logs, 'approvalAmount'));
            $deductibleSum = 0;
            $nonDeductibleSum = 0;
            foreach ($logs as $log) {
                if ($log['deductionType'] === 'non_deductible') {
                    $nonDeductibleSum += $log['approvalAmount'];
                } else {
                    $deductibleSum += $log['approvalAmount'];
                }
            }
            $journaledCount = count($journaledKeys);

            // 카드 목록 (드롭다운용)
            $cards = CardTransaction::where('tenant_id', $tenantId)
                ->select('card_num', 'card_company_name')
                ->distinct()
                ->get()
                ->toArray();

            return response()->json([
                'success' => true,
                'data' => [
                    'logs' => $logs,
                    'cards' => $cards,
                    'summary' => [
                        'totalCount' => $totalCount,
                        'totalAmount' => $totalAmount,
                        'deductibleSum' => $deductibleSum,
                        'nonDeductibleSum' => $nonDeductibleSum,
                    ],
                    'journalStats' => [
                        'journaledCount' => $journaledCount,
                        'unjournaledCount' => $totalCount - $journaledCount,
                    ],
                ],
            ]);
        } catch (\Throwable $e) {
            Log::error('카드거래 목록 조회 오류: '.$e->getMessage());

            return response()->json([
                'success' => false,
                'message' => '카드거래 목록 조회 실패: '.$e->getMessage(),
            ], 500);
        }
    }

    /**
     * 카드거래 기반 전표 생성
     */
    public function storeFromCard(Request $request): JsonResponse
    {
        $request->validate([
            'source_key' => 'required|string|max:255',
            'entry_date' => 'required|date',
            'description' => 'nullable|string|max:500',
            'lines' => 'required|array|min:2',
            'lines.*.dc_type' => 'required|in:debit,credit',
            'lines.*.account_code' => 'required|string|max:10',
            'lines.*.account_name' => 'required|string|max:100',
            'lines.*.trading_partner_id' => 'nullable|integer',
            'lines.*.trading_partner_name' => 'nullable|string|max:100',
            'lines.*.debit_amount' => 'required|integer|min:0',
            'lines.*.credit_amount' => 'required|integer|min:0',
            'lines.*.description' => 'nullable|string|max:300',
        ]);

        $tenantId = session('selected_tenant_id', 1);
        $lines = $request->lines;

        $totalDebit = collect($lines)->sum('debit_amount');
        $totalCredit = collect($lines)->sum('credit_amount');

        if ($totalDebit !== $totalCredit || $totalDebit === 0) {
            return response()->json([
                'success' => false,
                'message' => '차변합계와 대변합계가 일치해야 하며 0보다 커야 합니다.',
            ], 422);
        }

        // 중복 분개 체크
        $existing = JournalEntry::getJournalBySourceKey($tenantId, 'ecard_transaction', $request->source_key);
        if ($existing) {
            return response()->json([
                'success' => false,
                'message' => '이미 분개가 완료된 거래입니다. (전표번호: '.$existing->entry_no.')',
            ], 422);
        }

        $maxRetries = 3;
        $lastError = null;

        for ($attempt = 0; $attempt < $maxRetries; $attempt++) {
            try {
                $entry = DB::transaction(function () use ($tenantId, $request, $lines, $totalDebit, $totalCredit) {
                    $entryNo = JournalEntry::generateEntryNo($tenantId, $request->entry_date);

                    $entry = JournalEntry::create([
                        'tenant_id' => $tenantId,
                        'entry_no' => $entryNo,
                        'entry_date' => $request->entry_date,
                        'description' => $request->description,
                        'total_debit' => $totalDebit,
                        'total_credit' => $totalCredit,
                        'status' => 'draft',
                        'source_type' => 'ecard_transaction',
                        'source_key' => $request->source_key,
                        'created_by_name' => auth()->user()?->name ?? '시스템',
                    ]);

                    foreach ($lines as $i => $line) {
                        JournalEntryLine::create([
                            'tenant_id' => $tenantId,
                            'journal_entry_id' => $entry->id,
                            'line_no' => $i + 1,
                            'dc_type' => $line['dc_type'],
                            'account_code' => $line['account_code'],
                            'account_name' => $line['account_name'],
                            'trading_partner_id' => $line['trading_partner_id'] ?? null,
                            'trading_partner_name' => $line['trading_partner_name'] ?? null,
                            'debit_amount' => $line['debit_amount'],
                            'credit_amount' => $line['credit_amount'],
                            'description' => $line['description'] ?? null,
                        ]);
                    }

                    return $entry;
                });

                return response()->json([
                    'success' => true,
                    'message' => '분개가 저장되었습니다.',
                    'data' => ['id' => $entry->id, 'entry_no' => $entry->entry_no],
                ]);
            } catch (\Illuminate\Database\QueryException $e) {
                $lastError = $e;
                if ($e->errorInfo[1] === 1062) {
                    continue;
                }
                break;
            } catch (\Throwable $e) {
                $lastError = $e;
                break;
            }
        }

        Log::error('카드거래 분개 저장 오류: '.$lastError->getMessage());

        return response()->json([
            'success' => false,
            'message' => '분개 저장 실패: '.$lastError->getMessage(),
        ], 500);
    }

    /**
     * 특정 카드거래의 기존 분개 조회
     */
    public function cardJournals(Request $request): JsonResponse
    {
        $tenantId = session('selected_tenant_id', 1);
        $sourceKey = $request->get('source_key');

        if (! $sourceKey) {
            return response()->json(['success' => false, 'message' => 'source_key가 필요합니다.'], 422);
        }

        $entry = JournalEntry::forTenant($tenantId)
            ->where('source_type', 'ecard_transaction')
            ->where('source_key', $sourceKey)
            ->with('lines')
            ->first();

        if (! $entry) {
            return response()->json(['success' => true, 'data' => null]);
        }

        return response()->json([
            'success' => true,
            'data' => [
                'id' => $entry->id,
                'entry_no' => $entry->entry_no,
                'entry_date' => $entry->entry_date->format('Y-m-d'),
                'description' => $entry->description,
                'total_debit' => $entry->total_debit,
                'total_credit' => $entry->total_credit,
                'status' => $entry->status,
                'lines' => $entry->lines->map(function ($line) {
                    return [
                        'id' => $line->id,
                        'line_no' => $line->line_no,
                        'dc_type' => $line->dc_type,
                        'account_code' => $line->account_code,
                        'account_name' => $line->account_name,
                        'trading_partner_id' => $line->trading_partner_id,
                        'trading_partner_name' => $line->trading_partner_name,
                        'debit_amount' => $line->debit_amount,
                        'credit_amount' => $line->credit_amount,
                        'description' => $line->description,
                    ];
                }),
            ],
        ]);
    }

    /**
     * 카드거래 분개 삭제 (soft delete)
     */
    public function deleteCardJournal(int $id): JsonResponse
    {
        $tenantId = session('selected_tenant_id', 1);

        $entry = JournalEntry::forTenant($tenantId)
            ->where('source_type', 'ecard_transaction')
            ->findOrFail($id);

        $entry->delete();

        return response()->json([
            'success' => true,
            'message' => '분개가 삭제되었습니다.',
        ]);
    }
}
