<?php

namespace App\Services;

use App\Models\Tenants\AccountCode;
use App\Models\Tenants\JournalEntry;
use App\Models\Tenants\JournalEntryLine;
use App\Traits\SyncsExpenseAccounts;
use Illuminate\Support\Facades\DB;

/**
 * 세금계산서/카드거래 등 외부 소스의 분개 통합 관리 서비스
 *
 * journal_entries + journal_entry_lines에 저장하고
 * 복리후생비/접대비는 expense_accounts에 동기화 (CEO 대시보드)
 */
class JournalSyncService extends Service
{
    use SyncsExpenseAccounts;

    /**
     * 소스에 대한 분개 저장 (생성 또는 교체)
     *
     * @param  string  $sourceType  JournalEntry::SOURCE_TAX_INVOICE 등
     * @param  string  $sourceKey   'tax_invoice_123' 등
     * @param  string  $entryDate   전표일자 (Y-m-d)
     * @param  string|null  $description  적요
     * @param  array  $rows  분개 행 [{side, account_code, account_name?, debit_amount, credit_amount, vendor_id?, vendor_name?, memo?}]
     */
    public function saveForSource(
        string $sourceType,
        string $sourceKey,
        string $entryDate,
        ?string $description,
        array $rows,
    ): JournalEntry {
        $tenantId = $this->tenantId();

        return DB::transaction(function () use ($sourceType, $sourceKey, $entryDate, $description, $rows, $tenantId) {
            // 기존 전표가 있으면 삭제 후 재생성 (교체 방식)
            $existing = JournalEntry::query()
                ->where('tenant_id', $tenantId)
                ->where('source_type', $sourceType)
                ->where('source_key', $sourceKey)
                ->first();

            if ($existing) {
                $this->cleanupExpenseAccounts($tenantId, $existing->id);
                JournalEntryLine::where('journal_entry_id', $existing->id)->delete();
                $existing->forceDelete();
            }

            // 합계 계산
            $totalDebit = 0;
            $totalCredit = 0;
            foreach ($rows as $row) {
                $totalDebit += (int) ($row['debit_amount'] ?? 0);
                $totalCredit += (int) ($row['credit_amount'] ?? 0);
            }

            // 전표번호 생성
            $entryNo = $this->generateEntryNo($tenantId, $entryDate);

            // 전표 생성
            $entry = new JournalEntry;
            $entry->tenant_id = $tenantId;
            $entry->entry_no = $entryNo;
            $entry->entry_date = $entryDate;
            $entry->entry_type = JournalEntry::TYPE_GENERAL;
            $entry->description = $description;
            $entry->total_debit = $totalDebit;
            $entry->total_credit = $totalCredit;
            $entry->status = JournalEntry::STATUS_CONFIRMED;
            $entry->source_type = $sourceType;
            $entry->source_key = $sourceKey;
            $entry->save();

            // 분개 행 생성
            foreach ($rows as $index => $row) {
                $accountCode = $row['account_code'] ?? '';
                $accountName = $row['account_name'] ?? $this->resolveAccountName($tenantId, $accountCode);

                $line = new JournalEntryLine;
                $line->tenant_id = $tenantId;
                $line->journal_entry_id = $entry->id;
                $line->line_no = $index + 1;
                $line->dc_type = $row['side'];
                $line->account_code = $accountCode;
                $line->account_name = $accountName;
                $line->trading_partner_id = ! empty($row['vendor_id']) ? (int) $row['vendor_id'] : null;
                $line->trading_partner_name = $row['vendor_name'] ?? '';
                $line->debit_amount = (int) ($row['debit_amount'] ?? 0);
                $line->credit_amount = (int) ($row['credit_amount'] ?? 0);
                $line->description = $row['memo'] ?? null;
                $line->save();
            }

            // expense_accounts 동기화 (복리후생비/접대비 → CEO 대시보드)
            $this->syncExpenseAccounts($entry);

            return $entry->load('lines');
        });
    }

    /**
     * 소스에 대한 분개 조회
     */
    public function getForSource(string $sourceType, string $sourceKey): ?array
    {
        $tenantId = $this->tenantId();

        $entry = JournalEntry::query()
            ->where('tenant_id', $tenantId)
            ->where('source_type', $sourceType)
            ->where('source_key', $sourceKey)
            ->whereNull('deleted_at')
            ->with('lines')
            ->first();

        if (! $entry) {
            return null;
        }

        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,
            'rows' => $entry->lines->map(function ($line) {
                return [
                    'id' => $line->id,
                    'side' => $line->dc_type,
                    'account_code' => $line->account_code,
                    'account_name' => $line->account_name,
                    'vendor_id' => $line->trading_partner_id,
                    'vendor_name' => $line->trading_partner_name ?? '',
                    'debit_amount' => (int) $line->debit_amount,
                    'credit_amount' => (int) $line->credit_amount,
                    'memo' => $line->description ?? '',
                ];
            })->toArray(),
        ];
    }

    /**
     * 소스에 대한 분개 삭제
     */
    public function deleteForSource(string $sourceType, string $sourceKey): bool
    {
        $tenantId = $this->tenantId();

        return DB::transaction(function () use ($sourceType, $sourceKey, $tenantId) {
            $entry = JournalEntry::query()
                ->where('tenant_id', $tenantId)
                ->where('source_type', $sourceType)
                ->where('source_key', $sourceKey)
                ->first();

            if (! $entry) {
                return false;
            }

            $this->cleanupExpenseAccounts($tenantId, $entry->id);
            JournalEntryLine::where('journal_entry_id', $entry->id)->delete();
            $entry->delete(); // soft delete

            return true;
        });
    }

    /**
     * 전표번호 생성: JE-YYYYMMDD-NNN
     */
    private function generateEntryNo(int $tenantId, string $date): string
    {
        $dateStr = str_replace('-', '', substr($date, 0, 10));
        $prefix = "JE-{$dateStr}-";

        $lastEntry = DB::table('journal_entries')
            ->where('tenant_id', $tenantId)
            ->where('entry_no', 'like', "{$prefix}%")
            ->lockForUpdate()
            ->orderBy('entry_no', 'desc')
            ->first(['entry_no']);

        if ($lastEntry) {
            $lastSeq = (int) substr($lastEntry->entry_no, -3);
            $nextSeq = $lastSeq + 1;
        } else {
            $nextSeq = 1;
        }

        return $prefix . str_pad($nextSeq, 3, '0', STR_PAD_LEFT);
    }

    /**
     * 계정과목 코드 → 이름 조회
     */
    private function resolveAccountName(int $tenantId, string $code): string
    {
        if (empty($code)) {
            return '';
        }

        $account = AccountCode::query()
            ->where('tenant_id', $tenantId)
            ->where('code', $code)
            ->first(['name']);

        return $account ? $account->name : $code;
    }
}
