<?php

namespace App\Http\Controllers\Barobill;

use App\Http\Controllers\Controller;
use App\Models\Barobill\BarobillConfig;
use App\Models\Barobill\BarobillMember;
use App\Models\Tenants\Tenant;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;

/**
 * 바로빌 전자세금계산서 컨트롤러
 */
class EtaxController extends Controller
{
    /**
     * 바로빌 SOAP 설정
     */
    private ?string $certKey = null;

    private ?string $corpNum = null;

    private bool $isTestMode = false;

    private ?string $soapUrl = null;

    private ?\SoapClient $soapClient = null;

    public function __construct()
    {
        // DB에서 활성화된 바로빌 설정 조회
        $activeConfig = BarobillConfig::where('is_active', true)->first();

        if ($activeConfig) {
            $this->certKey = $activeConfig->cert_key;
            $this->corpNum = $activeConfig->corp_num;
            $this->isTestMode = $activeConfig->environment === 'test';
            $this->soapUrl = $activeConfig->base_url.'/TI.asmx?WSDL';
        } else {
            // 설정이 없으면 기본값 사용
            $this->isTestMode = config('services.barobill.test_mode', true);
            // 테스트 모드에 따라 적절한 CERT_KEY 선택
            $this->certKey = $this->isTestMode
                ? config('services.barobill.cert_key_test', '')
                : config('services.barobill.cert_key_prod', '');
            $this->corpNum = config('services.barobill.corp_num', '');
            $this->soapUrl = $this->isTestMode
                ? 'https://testws.baroservice.com/TI.asmx?WSDL'
                : 'https://ws.baroservice.com/TI.asmx?WSDL';
        }

        $this->initSoapClient();
    }

    /**
     * SOAP 클라이언트 초기화
     */
    private function initSoapClient(): void
    {
        if (! empty($this->certKey) || $this->isTestMode) {
            try {
                $context = stream_context_create([
                    'ssl' => [
                        'verify_peer' => false,
                        'verify_peer_name' => false,
                        'allow_self_signed' => true,
                    ],
                ]);

                $this->soapClient = new \SoapClient($this->soapUrl, [
                    'trace' => true,
                    'encoding' => 'UTF-8',
                    'exceptions' => true,
                    'connection_timeout' => 30,
                    'stream_context' => $context,
                    'cache_wsdl' => WSDL_CACHE_NONE,
                ]);
            } catch (\Throwable $e) {
                Log::error('바로빌 SOAP 클라이언트 생성 실패: '.$e->getMessage());
            }
        }
    }

    /**
     * 전자세금계산서 메인 페이지
     */
    public function index(Request $request): View|Response
    {
        if ($request->header('HX-Request')) {
            return response('', 200)->header('HX-Redirect', route('barobill.etax.index'));
        }

        // 현재 선택된 테넌트 정보
        $tenantId = session('selected_tenant_id');
        $currentTenant = $tenantId ? Tenant::find($tenantId) : null;

        // 해당 테넌트의 바로빌 회원사 정보 (공급자 정보로 사용)
        $barobillMember = $tenantId
            ? BarobillMember::where('tenant_id', $tenantId)->first()
            : null;

        // 테넌트별 서버 모드 적용 (회원사 설정 우선)
        $isTestMode = $barobillMember ? $barobillMember->isTestMode() : $this->isTestMode;

        // 서버 모드에 따라 SOAP 설정 재초기화
        if ($barobillMember && $barobillMember->server_mode) {
            $this->applyMemberServerMode($barobillMember);
        }

        return view('barobill.etax.index', [
            'certKey' => $this->certKey,
            'corpNum' => $this->corpNum,
            'isTestMode' => $isTestMode,
            'hasSoapClient' => $this->soapClient !== null,
            'currentTenant' => $currentTenant,
            'barobillMember' => $barobillMember,
        ]);
    }

    /**
     * 회원사 서버 모드에 따라 SOAP 설정 적용
     */
    private function applyMemberServerMode(BarobillMember $member): void
    {
        $memberTestMode = $member->isTestMode();
        $targetEnv = $memberTestMode ? 'test' : 'production';

        // 해당 환경의 BarobillConfig 조회 (is_active 무관하게 환경에 맞는 설정 사용)
        $config = BarobillConfig::where('environment', $targetEnv)->first();

        if ($config) {
            $this->isTestMode = $memberTestMode;
            $this->certKey = $config->cert_key;
            $this->corpNum = $config->corp_num;
            $baseUrl = $config->base_url ?: ($memberTestMode
                ? 'https://testws.baroservice.com'
                : 'https://ws.baroservice.com');
            $this->soapUrl = $baseUrl.'/TI.asmx?WSDL';

            // SOAP 클라이언트 재초기화
            $this->initSoapClient();

            Log::info('[Etax] 서버 모드 적용', [
                'targetEnv' => $targetEnv,
                'certKey' => substr($this->certKey ?? '', 0, 10).'...',
                'corpNum' => $this->corpNum,
                'soapUrl' => $this->soapUrl,
            ]);
        } else {
            Log::warning('[Etax] BarobillConfig 없음', ['targetEnv' => $targetEnv]);
        }
    }

    // 바로빌 파트너사 (본사) 테넌트 ID
    private const HEADQUARTERS_TENANT_ID = 1;

    /**
     * 세금계산서 목록 조회
     * - 테넌트 1(본사)이면 모든 세금계산서 표시
     * - 다른 테넌트면 해당 테넌트의 세금계산서만 표시
     */
    public function getInvoices(): JsonResponse
    {
        $tenantId = session('selected_tenant_id');
        $isHeadquarters = $tenantId == self::HEADQUARTERS_TENANT_ID;

        // 데이터 파일에서 조회 (실제 구현 시 DB 사용)
        $dataFile = storage_path('app/barobill/invoices_data.json');
        $invoices = [];

        if (file_exists($dataFile)) {
            $data = json_decode(file_get_contents($dataFile), true);
            $allInvoices = $data['invoices'] ?? [];

            // 본사(테넌트 1)가 아니면 해당 테넌트의 세금계산서만 필터링
            if (! $isHeadquarters && $tenantId) {
                $invoices = array_values(array_filter($allInvoices, function ($invoice) use ($tenantId) {
                    return ($invoice['tenant_id'] ?? null) == $tenantId;
                }));
            } else {
                $invoices = $allInvoices;
            }
        }

        return response()->json([
            'success' => true,
            'invoices' => $invoices,
            'tenant_id' => $tenantId,
            'is_headquarters' => $isHeadquarters,
        ]);
    }

    /**
     * 세금계산서 발행
     */
    public function issue(Request $request): JsonResponse
    {
        // 테넌트별 서버 모드 적용
        $tenantId = session('selected_tenant_id', self::HEADQUARTERS_TENANT_ID);
        $barobillMember = BarobillMember::where('tenant_id', $tenantId)->first();
        if ($barobillMember && $barobillMember->server_mode) {
            $this->applyMemberServerMode($barobillMember);
        }

        $input = $request->all();

        $useRealAPI = $this->soapClient !== null && ($this->isTestMode || ! empty($this->certKey));

        $debugInfo = [
            'hasSoapClient' => $this->soapClient !== null,
            'hasCertKey' => ! empty($this->certKey),
            'hasCorpNum' => ! empty($this->corpNum),
            'isTestMode' => $this->isTestMode,
            'willUseRealAPI' => $useRealAPI,
        ];

        if ($useRealAPI) {
            $apiResult = $this->issueTaxInvoice($input);

            if ($apiResult['success']) {
                $mgtKey = $input['issueKey'] ?? 'MGT'.date('YmdHis').rand(1000, 9999);

                $newInvoice = $this->createInvoiceRecord($input, $mgtKey, $apiResult['data'] ?? null);
                $this->saveInvoice($newInvoice);

                return response()->json([
                    'success' => true,
                    'message' => '세금계산서가 성공적으로 발행되었습니다.',
                    'data' => [
                        'issueKey' => $newInvoice['issueKey'],
                        'mgtKey' => $mgtKey,
                        'status' => 'issued',
                    ],
                    'invoice' => $newInvoice,
                    'simulation' => false,
                    'debug' => $debugInfo,
                ]);
            } else {
                return response()->json([
                    'success' => false,
                    'error' => $apiResult['error'] ?? 'API 호출 실패',
                    'error_code' => $apiResult['error_code'] ?? null,
                    'debug' => $debugInfo,
                ], 400);
            }
        } else {
            // 시뮬레이션 모드
            $issueKey = 'BARO-'.date('Y').'-'.str_pad(rand(1, 9999), 4, '0', STR_PAD_LEFT);

            $newInvoice = $this->createInvoiceRecord($input, $issueKey, null);
            $this->saveInvoice($newInvoice);

            return response()->json([
                'success' => true,
                'message' => '세금계산서가 성공적으로 발행되었습니다. (시뮬레이션 모드)',
                'data' => [
                    'issueKey' => $issueKey,
                    'status' => 'issued',
                ],
                'invoice' => $newInvoice,
                'simulation' => true,
                'debug' => $debugInfo,
                'warning' => '시뮬레이션 모드입니다. 실제 바로빌 API를 호출하려면 CERTKEY를 설정하세요.',
            ]);
        }
    }

    /**
     * 세금계산서 국세청 전송
     */
    public function sendToNts(Request $request): JsonResponse
    {
        // 테넌트별 서버 모드 적용
        $tenantId = session('selected_tenant_id', self::HEADQUARTERS_TENANT_ID);
        $barobillMember = BarobillMember::where('tenant_id', $tenantId)->first();
        if ($barobillMember && $barobillMember->server_mode) {
            $this->applyMemberServerMode($barobillMember);
        }

        $invoiceId = $request->input('invoiceId');

        // 인보이스 조회
        $dataFile = storage_path('app/barobill/invoices_data.json');
        $data = json_decode(file_get_contents($dataFile), true) ?? ['invoices' => []];

        $invoice = null;
        $invoiceIndex = null;
        foreach ($data['invoices'] as $index => $inv) {
            if ($inv['id'] === $invoiceId) {
                $invoice = $inv;
                $invoiceIndex = $index;
                break;
            }
        }

        if (! $invoice) {
            return response()->json([
                'success' => false,
                'error' => '세금계산서를 찾을 수 없습니다.',
            ], 404);
        }

        $useRealAPI = $this->soapClient !== null && ! empty($this->certKey);

        if ($useRealAPI && ! empty($invoice['mgtKey'])) {
            $result = $this->callBarobillSOAP('SendToNTS', [
                'CorpNum' => $this->corpNum,
                'MgtKey' => $invoice['mgtKey'],
            ]);

            if ($result['success']) {
                $data['invoices'][$invoiceIndex]['status'] = 'sent';
                $data['invoices'][$invoiceIndex]['ntsReceiptNo'] = 'NTS-'.date('YmdHis');
                $data['invoices'][$invoiceIndex]['sentAt'] = date('Y-m-d');
                file_put_contents($dataFile, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));

                return response()->json([
                    'success' => true,
                    'message' => '국세청 전송이 완료되었습니다.',
                ]);
            } else {
                return response()->json([
                    'success' => false,
                    'error' => $result['error'] ?? '전송 실패',
                ], 400);
            }
        } else {
            // 시뮬레이션
            $data['invoices'][$invoiceIndex]['status'] = 'sent';
            $data['invoices'][$invoiceIndex]['ntsReceiptNo'] = 'NTS-SIM-'.date('YmdHis');
            $data['invoices'][$invoiceIndex]['sentAt'] = date('Y-m-d');
            file_put_contents($dataFile, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));

            return response()->json([
                'success' => true,
                'message' => '국세청 전송이 완료되었습니다. (시뮬레이션)',
                'simulation' => true,
            ]);
        }
    }

    /**
     * 공급자 기초정보 조회
     */
    public function getSupplier(): JsonResponse
    {
        $tenantId = session('selected_tenant_id');
        if (! $tenantId) {
            return response()->json(['success' => false, 'error' => '테넌트가 선택되지 않았습니다.'], 400);
        }

        $member = BarobillMember::where('tenant_id', $tenantId)->first();
        if (! $member) {
            return response()->json(['success' => false, 'error' => '바로빌 회원사 정보가 없습니다.'], 404);
        }

        return response()->json([
            'success' => true,
            'supplier' => [
                'bizno' => $member->biz_no,
                'name' => $member->corp_name ?? '',
                'ceo' => $member->ceo_name ?? '',
                'addr' => $member->addr ?? '',
                'bizType' => $member->biz_type ?? '',
                'bizClass' => $member->biz_class ?? '',
                'contact' => $member->manager_name ?? '',
                'contactPhone' => $member->manager_hp ?? '',
                'email' => $member->manager_email ?? '',
            ],
        ]);
    }

    /**
     * 공급자 기초정보 수정
     */
    public function updateSupplier(Request $request): JsonResponse
    {
        $tenantId = session('selected_tenant_id');
        if (! $tenantId) {
            return response()->json(['success' => false, 'error' => '테넌트가 선택되지 않았습니다.'], 400);
        }

        $member = BarobillMember::where('tenant_id', $tenantId)->first();
        if (! $member) {
            return response()->json(['success' => false, 'error' => '바로빌 회원사 정보가 없습니다.'], 404);
        }

        $validated = $request->validate([
            'corp_name' => 'required|string|max:100',
            'ceo_name' => 'required|string|max:50',
            'addr' => 'nullable|string|max:255',
            'biz_type' => 'nullable|string|max:100',
            'biz_class' => 'nullable|string|max:100',
            'manager_name' => 'nullable|string|max:50',
            'manager_email' => 'nullable|email|max:100',
            'manager_hp' => 'nullable|string|max:20',
        ]);

        $member->update($validated);

        return response()->json([
            'success' => true,
            'message' => '공급자 정보가 수정되었습니다.',
            'supplier' => [
                'bizno' => $member->biz_no,
                'name' => $member->corp_name ?? '',
                'ceo' => $member->ceo_name ?? '',
                'addr' => $member->addr ?? '',
                'bizType' => $member->biz_type ?? '',
                'bizClass' => $member->biz_class ?? '',
                'contact' => $member->manager_name ?? '',
                'contactPhone' => $member->manager_hp ?? '',
                'email' => $member->manager_email ?? '',
            ],
        ]);
    }

    /**
     * 세금계산서 삭제
     */
    public function delete(Request $request): JsonResponse
    {
        $invoiceId = $request->input('invoiceId');

        $dataFile = storage_path('app/barobill/invoices_data.json');

        if (! file_exists($dataFile)) {
            return response()->json([
                'success' => false,
                'error' => '데이터 파일이 없습니다.',
            ], 404);
        }

        $data = json_decode(file_get_contents($dataFile), true) ?? ['invoices' => []];

        $originalCount = count($data['invoices']);
        $data['invoices'] = array_values(array_filter($data['invoices'], fn ($inv) => $inv['id'] !== $invoiceId));

        if (count($data['invoices']) === $originalCount) {
            return response()->json([
                'success' => false,
                'error' => '세금계산서를 찾을 수 없습니다.',
            ], 404);
        }

        file_put_contents($dataFile, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));

        return response()->json([
            'success' => true,
            'message' => '세금계산서가 삭제되었습니다.',
        ]);
    }

    /**
     * 바로빌 SOAP API 호출
     */
    private function callBarobillSOAP(string $method, array $params = []): array
    {
        if (! $this->soapClient) {
            return [
                'success' => false,
                'error' => '바로빌 SOAP 클라이언트가 초기화되지 않았습니다.',
            ];
        }

        if (! isset($params['CERTKEY'])) {
            $params['CERTKEY'] = $this->certKey;
        }

        try {
            $result = $this->soapClient->$method($params);
            $resultProperty = $method.'Result';

            if (isset($result->$resultProperty)) {
                $resultData = $result->$resultProperty;

                if (is_numeric($resultData) && $resultData < 0) {
                    return [
                        'success' => false,
                        'error' => '바로빌 API 오류 코드: '.$resultData,
                        'error_code' => $resultData,
                    ];
                }

                return [
                    'success' => true,
                    'data' => $resultData,
                ];
            }

            return [
                'success' => true,
                'data' => $result,
            ];
        } catch (\SoapFault $e) {
            return [
                'success' => false,
                'error' => 'SOAP 오류: '.$e->getMessage(),
            ];
        } catch (\Throwable $e) {
            return [
                'success' => false,
                'error' => 'API 호출 오류: '.$e->getMessage(),
            ];
        }
    }

    /**
     * 세금계산서 발행 API 호출
     */
    private function issueTaxInvoice(array $invoiceData): array
    {
        $mgtKey = $invoiceData['issueKey'] ?? 'MGT'.date('YmdHis').rand(1000, 9999);

        $supplyAmt = 0;
        $vat = 0;
        foreach ($invoiceData['items'] ?? [] as $item) {
            $supplyAmt += floatval($item['supplyAmt'] ?? 0);
            $vat += floatval($item['vat'] ?? 0);
        }
        $total = $supplyAmt + $vat;

        $taxType = $vat == 0 ? 2 : 1;

        $taxInvoice = [
            'IssueDirection' => 1, // 1: 정발행
            'TaxInvoiceType' => 1, // 1: 세금계산서
            'ModifyCode' => '', // 수정사유코드 (신규발행시 빈값)
            'TaxType' => $taxType, // 1: 과세, 2: 영세, 3: 면세
            'TaxCalcType' => 1, // 1: 소계합계
            'PurposeType' => 2, // 2: 청구
            'WriteDate' => date('Ymd', strtotime($invoiceData['supplyDate'] ?? date('Y-m-d'))),
            'AmountTotal' => number_format($supplyAmt, 0, '', ''),
            'TaxTotal' => number_format($vat, 0, '', ''),
            'TotalAmount' => number_format($total, 0, '', ''),
            'Cash' => '0',
            'ChkBill' => '0',
            'Note' => '0',
            'Credit' => number_format($total, 0, '', ''),
            'Remark1' => $invoiceData['memo'] ?? '',
            'Remark2' => '',
            'Remark3' => '',
            'Kwon' => '',
            'Ho' => '',
            'SerialNum' => '',
            'InvoicerParty' => [
                'MgtNum' => $mgtKey,
                'CorpNum' => $this->corpNum,
                'TaxRegID' => '', // 종사업장번호
                'CorpName' => $invoiceData['supplierName'] ?? '',
                'CEOName' => $invoiceData['supplierCeo'] ?? '',
                'Addr' => $invoiceData['supplierAddr'] ?? '',
                'BizType' => '', // 업태
                'BizClass' => '', // 종목
                'ContactID' => $invoiceData['supplierContactId'] ?? 'cbx0913', // 바로빌 담당자 ID (필수)
                'ContactName' => $invoiceData['supplierContact'] ?? '',
                'TEL' => $invoiceData['supplierTel'] ?? '',
                'HP' => '',
                'Email' => $invoiceData['supplierEmail'] ?? '',
            ],
            'InvoiceeParty' => [
                'MgtNum' => '',
                'CorpNum' => str_replace('-', '', $invoiceData['recipientBizno'] ?? ''),
                'TaxRegID' => '',
                'CorpName' => $invoiceData['recipientName'] ?? '',
                'CEOName' => $invoiceData['recipientCeo'] ?? '',
                'Addr' => $invoiceData['recipientAddr'] ?? '',
                'BizType' => '',
                'BizClass' => '',
                'ContactID' => '',
                'ContactName' => $invoiceData['recipientContact'] ?? '',
                'TEL' => $invoiceData['recipientTel'] ?? '',
                'HP' => '',
                'Email' => $invoiceData['recipientEmail'] ?? '',
            ],
            'BrokerParty' => [], // 위수탁 거래시에만 사용
            'TaxInvoiceTradeLineItems' => ['TaxInvoiceTradeLineItem' => []],
        ];

        $year = substr($invoiceData['supplyDate'] ?? date('Y-m-d'), 0, 4);

        foreach ($invoiceData['items'] ?? [] as $item) {
            $month = str_pad($item['month'] ?? '', 2, '0', STR_PAD_LEFT);
            $day = str_pad($item['day'] ?? '', 2, '0', STR_PAD_LEFT);
            $purchaseExpiry = ($month && $day && $month !== '00' && $day !== '00')
                ? $year.$month.$day
                : '';

            $taxInvoice['TaxInvoiceTradeLineItems']['TaxInvoiceTradeLineItem'][] = [
                'PurchaseExpiry' => $purchaseExpiry, // 거래일자 (YYYYMMDD)
                'Name' => $item['name'] ?? '', // 품명
                'Information' => $item['spec'] ?? '', // 규격
                'ChargeableUnit' => $item['qty'] ?? '1', // 수량
                'UnitPrice' => number_format(floatval($item['unitPrice'] ?? 0), 0, '', ''), // 단가
                'Amount' => number_format(floatval($item['supplyAmt'] ?? 0), 0, '', ''), // 공급가액
                'Tax' => number_format(floatval($item['vat'] ?? 0), 0, '', ''), // 부가세
                'Description' => $item['description'] ?? '', // 비고
            ];
        }

        return $this->callBarobillSOAP('RegistAndIssueTaxInvoice', [
            'CorpNum' => $this->corpNum,
            'Invoice' => $taxInvoice,
            'SendSMS' => false,
            'ForceIssue' => false,
            'MailTitle' => '',
        ]);
    }

    /**
     * 인보이스 레코드 생성
     */
    private function createInvoiceRecord(array $input, string $issueKey, $apiData): array
    {
        return [
            'id' => 'inv_'.time().'_'.rand(1000, 9999),
            'tenant_id' => session('selected_tenant_id'), // 테넌트별 필터링용
            'issueKey' => $issueKey,
            'mgtKey' => $issueKey,
            'supplierBizno' => $input['supplierBizno'] ?? '',
            'supplierName' => $input['supplierName'] ?? '',
            'supplierCeo' => $input['supplierCeo'] ?? '',
            'supplierAddr' => $input['supplierAddr'] ?? '',
            'supplierContact' => $input['supplierContact'] ?? '',
            'supplierEmail' => $input['supplierEmail'] ?? '',
            'recipientBizno' => $input['recipientBizno'] ?? '',
            'recipientName' => $input['recipientName'] ?? '',
            'recipientCeo' => $input['recipientCeo'] ?? '',
            'recipientAddr' => $input['recipientAddr'] ?? '',
            'recipientContact' => $input['recipientContact'] ?? '',
            'recipientEmail' => $input['recipientEmail'] ?? '',
            'supplyDate' => $input['supplyDate'] ?? date('Y-m-d'),
            'items' => $input['items'] ?? [],
            'totalSupplyAmt' => $input['totalSupplyAmt'] ?? 0,
            'totalVat' => $input['totalVat'] ?? 0,
            'total' => $input['total'] ?? 0,
            'status' => 'issued',
            'memo' => $input['memo'] ?? '',
            'createdAt' => date('Y-m-d\TH:i:s'),
            'sentAt' => date('Y-m-d'),
            'barobillInvoiceId' => is_numeric($apiData) ? (string) $apiData : '',
        ];
    }

    /**
     * 인보이스 저장
     */
    private function saveInvoice(array $invoice): bool
    {
        $dataDir = storage_path('app/barobill');
        if (! is_dir($dataDir)) {
            mkdir($dataDir, 0755, true);
        }

        $dataFile = $dataDir.'/invoices_data.json';
        $existingData = ['invoices' => []];

        if (file_exists($dataFile)) {
            $content = file_get_contents($dataFile);
            if ($content) {
                $existingData = json_decode($content, true) ?? ['invoices' => []];
            }
        }

        $existingData['invoices'][] = $invoice;

        if (count($existingData['invoices']) > 100) {
            $existingData['invoices'] = array_slice($existingData['invoices'], -100);
        }

        return file_put_contents($dataFile, json_encode($existingData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)) !== false;
    }
}
