<?php

namespace App\Http\Controllers\Finance;

use App\Http\Controllers\Controller;
use App\Models\Barobill\HometaxInvoiceJournal;
use App\Models\Finance\JournalEntryLine;
use App\Models\Finance\Payable;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class PayableController extends Controller
{
    public function index(Request $request): JsonResponse
    {
        $tenantId = session('selected_tenant_id', 1);

        $query = Payable::forTenant($tenantId);

        if ($search = $request->input('search')) {
            $query->where(function ($q) use ($search) {
                $q->where('vendor_name', 'like', "%{$search}%");
            });
        }

        if ($status = $request->input('status')) {
            if ($status !== 'all') {
                $query->where('status', $status);
            }
        }

        if ($category = $request->input('category')) {
            if ($category !== 'all') {
                $query->where('category', $category);
            }
        }

        $payables = $query->orderBy('created_at', 'desc')
            ->get()
            ->map(function ($item) {
                return [
                    'id' => $item->id,
                    'vendorName' => $item->vendor_name,
                    'invoiceNo' => $item->invoice_no,
                    'issueDate' => $item->issue_date?->format('Y-m-d'),
                    'dueDate' => $item->due_date?->format('Y-m-d'),
                    'category' => $item->category,
                    'amount' => $item->amount,
                    'paidAmount' => $item->paid_amount,
                    'status' => $item->status,
                    'description' => $item->description,
                    'memo' => $item->memo,
                    'taxInvoiceIssued' => (bool) $item->tax_invoice_issued,
                ];
            });

        $all = Payable::forTenant($tenantId)->get();

        $totalAmount = $all->sum('amount');
        $totalPaid = $all->sum('paid_amount');
        $overdueAmount = $all->where('status', 'overdue')->sum(function ($item) {
            return $item->amount - $item->paid_amount;
        });

        $stats = [
            'totalAmount' => $totalAmount,
            'totalPaid' => $totalPaid,
            'totalUnpaid' => $totalAmount - $totalPaid,
            'overdueAmount' => $overdueAmount,
            'count' => $all->count(),
        ];

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

    public function store(Request $request): JsonResponse
    {
        $request->validate([
            'vendorName' => 'required|string|max:100',
            'invoiceNo' => 'nullable|string|max:50',
            'amount' => 'required|integer|min:0',
        ]);

        $tenantId = session('selected_tenant_id', 1);

        Payable::create([
            'tenant_id' => $tenantId,
            'vendor_name' => $request->input('vendorName'),
            'invoice_no' => $request->input('invoiceNo'),
            'issue_date' => $request->input('issueDate'),
            'due_date' => $request->input('dueDate'),
            'category' => $request->input('category', '사무용품'),
            'amount' => $request->input('amount', 0),
            'paid_amount' => 0,
            'status' => 'unpaid',
            'description' => $request->input('description'),
            'memo' => $request->input('memo'),
            'tax_invoice_issued' => $request->boolean('taxInvoiceIssued', false),
        ]);

        return response()->json([
            'success' => true,
            'message' => '미지급금이 등록되었습니다.',
        ]);
    }

    public function update(Request $request, int $id): JsonResponse
    {
        $tenantId = session('selected_tenant_id', 1);
        $payable = Payable::forTenant($tenantId)->findOrFail($id);

        $request->validate([
            'vendorName' => 'required|string|max:100',
            'invoiceNo' => 'nullable|string|max:50',
            'amount' => 'required|integer|min:0',
        ]);

        $payable->update([
            'vendor_name' => $request->input('vendorName'),
            'invoice_no' => $request->input('invoiceNo'),
            'issue_date' => $request->input('issueDate'),
            'due_date' => $request->input('dueDate'),
            'category' => $request->input('category'),
            'amount' => $request->input('amount'),
            'status' => $request->input('status', $payable->status),
            'description' => $request->input('description'),
            'memo' => $request->input('memo'),
            'tax_invoice_issued' => $request->boolean('taxInvoiceIssued', $payable->tax_invoice_issued),
        ]);

        return response()->json([
            'success' => true,
            'message' => '미지급금이 수정되었습니다.',
        ]);
    }

    public function pay(Request $request, int $id): JsonResponse
    {
        $tenantId = session('selected_tenant_id', 1);
        $payable = Payable::forTenant($tenantId)->findOrFail($id);

        $request->validate([
            'payAmount' => 'required|integer|min:1',
        ]);

        $payAmount = $request->input('payAmount');
        $remaining = $payable->amount - $payable->paid_amount;

        if ($payAmount > $remaining) {
            return response()->json([
                'success' => false,
                'message' => '지급액이 잔액을 초과합니다.',
            ], 422);
        }

        $newPaid = $payable->paid_amount + $payAmount;
        $newStatus = $newPaid >= $payable->amount ? 'paid' : 'partial';

        $payable->update([
            'paid_amount' => $newPaid,
            'status' => $newStatus,
        ]);

        return response()->json([
            'success' => true,
            'message' => '지급 처리되었습니다.',
        ]);
    }

    public function destroy(int $id): JsonResponse
    {
        $tenantId = session('selected_tenant_id', 1);
        $payable = Payable::forTenant($tenantId)->findOrFail($id);
        $payable->delete();

        return response()->json([
            'success' => true,
            'message' => '미지급금이 삭제되었습니다.',
        ]);
    }

    /**
     * 통합 잔액 조회: 홈택스 발생 + 일반전표 상계를 거래처별로 집계
     */
    public function integrated(Request $request): JsonResponse
    {
        $tenantId = session('selected_tenant_id', 1);
        $startDate = $request->input('startDate', date('Y-01-01'));
        $endDate = $request->input('endDate', date('Y-12-31'));
        $account = $request->input('account', 'all');
        $vendor = $request->input('vendor', '');

        $accountCodes = $account === 'all' ? ['204', '205'] : [$account];

        // 0. 이월 잔액 계산 (startDate 이전의 누적 잔액)
        $priorBalanceMap = [];

        // 이월 - 홈택스 대변(발생)
        $priorHometax = HometaxInvoiceJournal::where('tenant_id', $tenantId)
            ->whereIn('account_code', $accountCodes)
            ->where('dc_type', 'credit')
            ->where('credit_amount', '>', 0)
            ->where('write_date', '<', $startDate);
        if ($vendor) {
            $priorHometax->where('trading_partner_name', 'like', "%{$vendor}%");
        }
        $priorHometax->select('trading_partner_name', 'account_code', 'account_name', DB::raw('SUM(credit_amount) as total_credit'))
            ->groupBy('trading_partner_name', 'account_code', 'account_name')
            ->get()
            ->each(function ($row) use (&$priorBalanceMap) {
                $key = $row->trading_partner_name.'|'.$row->account_code;
                if (! isset($priorBalanceMap[$key])) {
                    $priorBalanceMap[$key] = ['vendorName' => $row->trading_partner_name, 'accountCode' => $row->account_code, 'accountName' => $row->account_name, 'occurred' => 0, 'offset' => 0];
                }
                $priorBalanceMap[$key]['occurred'] += $row->total_credit;
            });

        // 이월 - 일반전표 차변(상계) + 대변(발생)
        $priorJournal = JournalEntryLine::where('journal_entry_lines.tenant_id', $tenantId)
            ->whereIn('journal_entry_lines.account_code', $accountCodes)
            ->join('journal_entries', function ($join) {
                $join->on('journal_entries.id', '=', 'journal_entry_lines.journal_entry_id')
                    ->whereNull('journal_entries.deleted_at');
            })
            ->where('journal_entries.entry_date', '<', $startDate);
        if ($vendor) {
            $priorJournal->where('journal_entry_lines.trading_partner_name', 'like', "%{$vendor}%");
        }
        $priorJournal->select(
            'journal_entry_lines.trading_partner_name',
            'journal_entry_lines.account_code',
            'journal_entry_lines.account_name',
            'journal_entry_lines.dc_type',
            DB::raw('SUM(journal_entry_lines.debit_amount) as total_debit'),
            DB::raw('SUM(journal_entry_lines.credit_amount) as total_credit')
        )
            ->groupBy('journal_entry_lines.trading_partner_name', 'journal_entry_lines.account_code', 'journal_entry_lines.account_name', 'journal_entry_lines.dc_type')
            ->get()
            ->each(function ($row) use (&$priorBalanceMap) {
                $key = $row->trading_partner_name.'|'.$row->account_code;
                if (! isset($priorBalanceMap[$key])) {
                    $priorBalanceMap[$key] = ['vendorName' => $row->trading_partner_name, 'accountCode' => $row->account_code, 'accountName' => $row->account_name, 'occurred' => 0, 'offset' => 0];
                }
                if ($row->dc_type === 'debit') {
                    $priorBalanceMap[$key]['offset'] += $row->total_debit;
                } else {
                    $priorBalanceMap[$key]['occurred'] += $row->total_credit;
                }
            });

        // 1. 홈택스 매입세금계산서 - 대변(미지급금 발생)
        $hometaxQuery = HometaxInvoiceJournal::where('tenant_id', $tenantId)
            ->whereIn('account_code', $accountCodes)
            ->where('dc_type', 'credit')
            ->where('credit_amount', '>', 0)
            ->whereBetween('write_date', [$startDate, $endDate]);

        if ($vendor) {
            $hometaxQuery->where('trading_partner_name', 'like', "%{$vendor}%");
        }

        $hometaxGrouped = (clone $hometaxQuery)
            ->select(
                'trading_partner_name',
                'account_code',
                'account_name',
                DB::raw('SUM(credit_amount) as total_credit'),
                DB::raw('COUNT(*) as cnt'),
                DB::raw('MIN(write_date) as first_date'),
                DB::raw('MAX(write_date) as last_date')
            )
            ->groupBy('trading_partner_name', 'account_code', 'account_name')
            ->orderByDesc('total_credit')
            ->get();

        $hometaxDetails = (clone $hometaxQuery)
            ->select('id', 'trading_partner_name', 'account_code', 'account_name', 'credit_amount', 'write_date', 'description', 'nts_confirm_num')
            ->orderByDesc('write_date')
            ->get();

        // 2. 일반전표 - 계정 204/205 차변(상계) + 대변(전표를 통한 발생)
        $journalQuery = JournalEntryLine::where('journal_entry_lines.tenant_id', $tenantId)
            ->whereIn('journal_entry_lines.account_code', $accountCodes)
            ->join('journal_entries', function ($join) {
                $join->on('journal_entries.id', '=', 'journal_entry_lines.journal_entry_id')
                    ->whereNull('journal_entries.deleted_at');
            })
            ->whereBetween('journal_entries.entry_date', [$startDate, $endDate]);

        if ($vendor) {
            $journalQuery->where('journal_entry_lines.trading_partner_name', 'like', "%{$vendor}%");
        }

        $journalGrouped = (clone $journalQuery)
            ->select(
                'journal_entry_lines.trading_partner_name',
                'journal_entry_lines.account_code',
                'journal_entry_lines.account_name',
                'journal_entry_lines.dc_type',
                DB::raw('SUM(journal_entry_lines.debit_amount) as total_debit'),
                DB::raw('SUM(journal_entry_lines.credit_amount) as total_credit'),
                DB::raw('COUNT(*) as cnt')
            )
            ->groupBy('journal_entry_lines.trading_partner_name', 'journal_entry_lines.account_code', 'journal_entry_lines.account_name', 'journal_entry_lines.dc_type')
            ->orderByDesc('total_debit')
            ->get();

        $journalDetails = (clone $journalQuery)
            ->select(
                'journal_entry_lines.id',
                'journal_entry_lines.trading_partner_name',
                'journal_entry_lines.account_code',
                'journal_entry_lines.account_name',
                'journal_entry_lines.dc_type',
                'journal_entry_lines.debit_amount',
                'journal_entry_lines.credit_amount',
                'journal_entry_lines.description',
                'journal_entries.entry_date',
                'journal_entries.entry_no'
            )
            ->orderByDesc('journal_entries.entry_date')
            ->get();

        // 3. 거래처별 잔액 계산 (이월 잔액 포함)
        $vendorMap = [];

        $initVendor = function ($key, $row) use (&$vendorMap, $priorBalanceMap) {
            if (! isset($vendorMap[$key])) {
                $prior = $priorBalanceMap[$key] ?? null;
                $priorBalance = $prior ? ($prior['occurred'] - $prior['offset']) : 0;
                $vendorMap[$key] = [
                    'vendorName' => $row->trading_partner_name,
                    'accountCode' => $row->account_code,
                    'accountName' => $row->account_name,
                    'priorBalance' => $priorBalance,
                    'occurred' => 0,
                    'offset' => 0,
                    'hometaxCount' => 0,
                    'journalDebitCount' => 0,
                    'journalCreditCount' => 0,
                ];
            }
        };

        // 홈택스 대변 = 발생
        foreach ($hometaxGrouped as $row) {
            $key = $row->trading_partner_name.'|'.$row->account_code;
            $initVendor($key, $row);
            $vendorMap[$key]['occurred'] += $row->total_credit;
            $vendorMap[$key]['hometaxCount'] += $row->cnt;
        }

        // 일반전표 차변 = 상계, 대변 = 발생
        foreach ($journalGrouped as $row) {
            $key = $row->trading_partner_name.'|'.$row->account_code;
            $initVendor($key, $row);
            if ($row->dc_type === 'debit') {
                $vendorMap[$key]['offset'] += $row->total_debit;
                $vendorMap[$key]['journalDebitCount'] += $row->cnt;
            } else {
                $vendorMap[$key]['occurred'] += $row->total_credit;
                $vendorMap[$key]['journalCreditCount'] += $row->cnt;
            }
        }

        // 이월 잔액만 있고 당기 거래가 없는 거래처도 포함
        foreach ($priorBalanceMap as $key => $prior) {
            if (! isset($vendorMap[$key])) {
                $priorBalance = $prior['occurred'] - $prior['offset'];
                if ($priorBalance != 0) {
                    $vendorMap[$key] = [
                        'vendorName' => $prior['vendorName'],
                        'accountCode' => $prior['accountCode'],
                        'accountName' => $prior['accountName'],
                        'priorBalance' => $priorBalance,
                        'occurred' => 0,
                        'offset' => 0,
                        'hometaxCount' => 0,
                        'journalDebitCount' => 0,
                        'journalCreditCount' => 0,
                    ];
                }
            }
        }

        // 잔액 계산 및 정렬: 이월잔액 + 당기발생 - 당기상계
        $byVendor = collect($vendorMap)->map(function ($item) {
            $item['balance'] = $item['priorBalance'] + $item['occurred'] - $item['offset'];

            return $item;
        })->sortByDesc('balance')->values()->all();

        // 4. 요약 통계
        $totalPriorBalance = array_sum(array_column($byVendor, 'priorBalance'));
        $totalOccurred = array_sum(array_column($byVendor, 'occurred'));
        $totalOffset = array_sum(array_column($byVendor, 'offset'));

        return response()->json([
            'success' => true,
            'data' => [
                'summary' => [
                    'totalPriorBalance' => $totalPriorBalance,
                    'totalOccurred' => $totalOccurred,
                    'totalOffset' => $totalOffset,
                    'totalBalance' => $totalPriorBalance + $totalOccurred - $totalOffset,
                    'hometaxCount' => $hometaxDetails->count(),
                    'journalCount' => $journalDetails->count(),
                    'vendorCount' => count($byVendor),
                ],
                'byVendor' => $byVendor,
                'hometaxDetails' => $hometaxDetails,
                'journalDetails' => $journalDetails,
            ],
        ]);
    }

    /**
     * 홈택스 매입세금계산서 미지급금/미지급비용 대변 내역
     */
    public function hometaxPayables(Request $request): JsonResponse
    {
        $tenantId = session('selected_tenant_id', 1);
        $startDate = $request->input('startDate', date('Y-01-01'));
        $endDate = $request->input('endDate', date('Y-12-31'));
        $account = $request->input('account', 'all');

        $accountCodes = $account === 'all' ? ['204', '205'] : [$account];

        $items = HometaxInvoiceJournal::where('tenant_id', $tenantId)
            ->whereIn('account_code', $accountCodes)
            ->where('dc_type', 'credit')
            ->where('credit_amount', '>', 0)
            ->whereBetween('write_date', [$startDate, $endDate])
            ->select('id', 'trading_partner_name', 'account_code', 'account_name', 'credit_amount', 'write_date', 'description', 'nts_confirm_num', 'supply_amount', 'tax_amount', 'total_amount')
            ->orderByDesc('write_date')
            ->get();

        $totalCredit = $items->sum('credit_amount');

        return response()->json([
            'success' => true,
            'data' => $items,
            'summary' => [
                'totalCredit' => $totalCredit,
                'count' => $items->count(),
            ],
        ]);
    }

    /**
     * 일반전표 미지급금/미지급비용 내역 (차변=상계, 대변=발생)
     */
    public function journalPayables(Request $request): JsonResponse
    {
        $tenantId = session('selected_tenant_id', 1);
        $startDate = $request->input('startDate', date('Y-01-01'));
        $endDate = $request->input('endDate', date('Y-12-31'));
        $account = $request->input('account', 'all');
        $dcType = $request->input('dcType', 'all');

        $accountCodes = $account === 'all' ? ['204', '205'] : [$account];

        $query = JournalEntryLine::where('journal_entry_lines.tenant_id', $tenantId)
            ->whereIn('journal_entry_lines.account_code', $accountCodes)
            ->join('journal_entries', function ($join) {
                $join->on('journal_entries.id', '=', 'journal_entry_lines.journal_entry_id')
                    ->whereNull('journal_entries.deleted_at');
            })
            ->whereBetween('journal_entries.entry_date', [$startDate, $endDate]);

        if ($dcType !== 'all') {
            $query->where('journal_entry_lines.dc_type', $dcType);
        }

        $items = $query->select(
            'journal_entry_lines.id',
            'journal_entry_lines.trading_partner_name',
            'journal_entry_lines.account_code',
            'journal_entry_lines.account_name',
            'journal_entry_lines.dc_type',
            'journal_entry_lines.debit_amount',
            'journal_entry_lines.credit_amount',
            'journal_entry_lines.description',
            'journal_entries.entry_date',
            'journal_entries.entry_no'
        )
            ->orderByDesc('journal_entries.entry_date')
            ->get();

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

        return response()->json([
            'success' => true,
            'data' => $items,
            'summary' => [
                'totalDebit' => $totalDebit,
                'totalCredit' => $totalCredit,
                'count' => $items->count(),
            ],
        ]);
    }
}
