<?php

namespace App\Services;

use App\Models\Tenants\BarobillSetting;
use App\Models\Tenants\TaxInvoice;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

/**
 * 바로빌 API 연동 서비스
 *
 * 바로빌 개발자센터: https://dev.barobill.co.kr/
 */
class BarobillService extends Service
{
    /**
     * 바로빌 API 기본 URL
     */
    private const API_BASE_URL = 'https://ws.barobill.co.kr';

    /**
     * 바로빌 API 테스트 URL
     */
    private const API_TEST_URL = 'https://testws.barobill.co.kr';

    /**
     * 테스트 모드 여부
     */
    private bool $testMode;

    public function __construct()
    {
        $this->testMode = config('services.barobill.test_mode', true);
    }

    // =========================================================================
    // 설정 관리
    // =========================================================================

    /**
     * 바로빌 설정 조회
     */
    public function getSetting(): ?BarobillSetting
    {
        $tenantId = $this->tenantId();

        return BarobillSetting::query()
            ->where('tenant_id', $tenantId)
            ->first();
    }

    /**
     * 바로빌 설정 저장
     */
    public function saveSetting(array $data): BarobillSetting
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        $setting = BarobillSetting::query()
            ->where('tenant_id', $tenantId)
            ->first();

        if ($setting) {
            $setting->fill(array_merge($data, ['updated_by' => $userId]));
            $setting->save();
        } else {
            $setting = BarobillSetting::create(array_merge($data, [
                'tenant_id' => $tenantId,
                'created_by' => $userId,
                'updated_by' => $userId,
            ]));
        }

        return $setting->fresh();
    }

    /**
     * 연동 테스트
     */
    public function testConnection(): array
    {
        $setting = $this->getSetting();

        if (! $setting || ! $setting->canConnect()) {
            throw new BadRequestHttpException(__('error.barobill.setting_not_configured'));
        }

        try {
            // 바로빌 API 토큰 조회로 연동 테스트
            $response = $this->callApi('GetAccessToken', [
                'CERTKEY' => $setting->cert_key,
                'CorpNum' => $setting->corp_num,
                'ID' => $setting->barobill_id,
            ]);

            if (! empty($response['AccessToken'])) {
                // 검증 성공 시 verified_at 업데이트
                $setting->verified_at = now();
                $setting->save();

                return [
                    'success' => true,
                    'message' => __('message.barobill.connection_success'),
                    'verified_at' => $setting->verified_at->toDateTimeString(),
                ];
            }

            throw new BadRequestHttpException($response['Message'] ?? __('error.barobill.connection_failed'));
        } catch (\Exception $e) {
            Log::error('바로빌 연동 테스트 실패', [
                'tenant_id' => $this->tenantId(),
                'error' => $e->getMessage(),
            ]);

            throw new BadRequestHttpException(__('error.barobill.connection_failed').': '.$e->getMessage());
        }
    }

    // =========================================================================
    // 사업자등록번호 검증
    // =========================================================================

    /**
     * 사업자등록번호 유효성 검사 (휴폐업 조회)
     *
     * 바로빌 API를 통해 사업자등록번호의 유효성을 검증합니다.
     * 바로빌 설정이 없는 경우 기본 형식 검증만 수행합니다.
     *
     * @param  string  $businessNumber  사업자등록번호 (10자리, 하이픈 제거)
     * @return array{valid: bool, status: string, status_label: string, corp_name: ?string, ceo_name: ?string, message: string}
     */
    public function checkBusinessNumber(string $businessNumber): array
    {
        // 하이픈 제거 및 숫자만 추출
        $businessNumber = preg_replace('/[^0-9]/', '', $businessNumber);

        // 기본 형식 검증 (10자리)
        if (strlen($businessNumber) !== 10) {
            return [
                'valid' => false,
                'status' => 'invalid_format',
                'status_label' => '형식 오류',
                'corp_name' => null,
                'ceo_name' => null,
                'message' => __('error.company.invalid_business_number_format'),
            ];
        }

        // 체크섬 검증 (사업자등록번호 자체 유효성)
        if (! $this->validateBusinessNumberChecksum($businessNumber)) {
            return [
                'valid' => false,
                'status' => 'invalid_checksum',
                'status_label' => '유효하지 않음',
                'corp_name' => null,
                'ceo_name' => null,
                'message' => __('error.company.invalid_business_number'),
            ];
        }

        // 바로빌 API 조회 시도
        try {
            $response = $this->callApi('CheckCorpNum', [
                'CorpNum' => $businessNumber,
            ]);

            // 바로빌 응답 해석
            if (isset($response['CorpState'])) {
                $state = $response['CorpState'];
                $isValid = in_array($state, ['01', '02']); // 01: 사업중, 02: 휴업
                $statusLabel = match ($state) {
                    '01' => '사업중',
                    '02' => '휴업',
                    '03' => '폐업',
                    default => '조회 불가',
                };

                return [
                    'valid' => $isValid,
                    'status' => $state,
                    'status_label' => $statusLabel,
                    'corp_name' => $response['CorpName'] ?? null,
                    'ceo_name' => $response['CEOName'] ?? null,
                    'message' => $isValid
                        ? __('message.company.business_number_valid')
                        : __('error.company.business_closed'),
                ];
            }

            // 응답 형식이 다른 경우 (결과 코드 방식)
            if (isset($response['Result'])) {
                $isValid = $response['Result'] >= 0;

                return [
                    'valid' => $isValid,
                    'status' => $isValid ? 'active' : 'unknown',
                    'status_label' => $isValid ? '유효함' : '조회 불가',
                    'corp_name' => $response['CorpName'] ?? null,
                    'ceo_name' => $response['CEOName'] ?? null,
                    'message' => $isValid
                        ? __('message.company.business_number_valid')
                        : ($response['Message'] ?? __('error.company.check_failed')),
                ];
            }

            // 기본 응답 (체크섬만 통과한 경우)
            return [
                'valid' => true,
                'status' => 'format_valid',
                'status_label' => '형식 유효',
                'corp_name' => null,
                'ceo_name' => null,
                'message' => __('message.company.business_number_format_valid'),
            ];
        } catch (\Exception $e) {
            // API 호출 실패 시 형식 검증 결과만 반환
            Log::warning('바로빌 사업자번호 조회 실패', [
                'business_number' => $businessNumber,
                'error' => $e->getMessage(),
            ]);

            return [
                'valid' => true,
                'status' => 'format_valid',
                'status_label' => '형식 유효 (API 조회 불가)',
                'corp_name' => null,
                'ceo_name' => null,
                'message' => __('message.company.business_number_format_valid'),
            ];
        }
    }

    /**
     * 사업자등록번호 체크섬 검증
     *
     * @param  string  $businessNumber  10자리 사업자등록번호
     */
    private function validateBusinessNumberChecksum(string $businessNumber): bool
    {
        if (strlen($businessNumber) !== 10) {
            return false;
        }

        $digits = str_split($businessNumber);
        $multipliers = [1, 3, 7, 1, 3, 7, 1, 3, 5];
        $sum = 0;

        for ($i = 0; $i < 9; $i++) {
            $sum += intval($digits[$i]) * $multipliers[$i];
        }

        // 8번째 자리 (인덱스 8)에 대한 추가 처리
        $sum += intval(floor(intval($digits[8]) * 5 / 10));

        $remainder = $sum % 10;
        $checkDigit = (10 - $remainder) % 10;

        return $checkDigit === intval($digits[9]);
    }

    // =========================================================================
    // 세금계산서 발행
    // =========================================================================

    /**
     * 세금계산서 발행
     */
    public function issueTaxInvoice(TaxInvoice $taxInvoice): TaxInvoice
    {
        $setting = $this->getSetting();

        if (! $setting || ! $setting->canConnect()) {
            throw new BadRequestHttpException(__('error.barobill.setting_not_configured'));
        }

        try {
            // 바로빌 API 호출을 위한 데이터 구성
            $apiData = $this->buildTaxInvoiceData($taxInvoice, $setting);

            // 세금계산서 발행 API 호출
            $response = $this->callApi('RegistAndIssueTaxInvoice', $apiData);

            if (! empty($response['InvoiceID'])) {
                // 발행 성공
                $taxInvoice->barobill_invoice_id = $response['InvoiceID'];
                $taxInvoice->nts_confirm_num = $response['NTSConfirmNum'] ?? null;
                $taxInvoice->status = TaxInvoice::STATUS_ISSUED;
                $taxInvoice->issued_at = now();
                $taxInvoice->error_message = null;
                $taxInvoice->save();

                Log::info('세금계산서 발행 성공', [
                    'tenant_id' => $this->tenantId(),
                    'tax_invoice_id' => $taxInvoice->id,
                    'barobill_invoice_id' => $response['InvoiceID'],
                ]);

                return $taxInvoice->fresh();
            }

            throw new \Exception($response['Message'] ?? '발행 실패');
        } catch (\Exception $e) {
            // 발행 실패
            $taxInvoice->status = TaxInvoice::STATUS_FAILED;
            $taxInvoice->error_message = $e->getMessage();
            $taxInvoice->save();

            Log::error('세금계산서 발행 실패', [
                'tenant_id' => $this->tenantId(),
                'tax_invoice_id' => $taxInvoice->id,
                'error' => $e->getMessage(),
            ]);

            throw new BadRequestHttpException(__('error.barobill.issue_failed').': '.$e->getMessage());
        }
    }

    /**
     * 세금계산서 취소
     */
    public function cancelTaxInvoice(TaxInvoice $taxInvoice, string $reason): TaxInvoice
    {
        $setting = $this->getSetting();

        if (! $setting || ! $setting->canConnect()) {
            throw new BadRequestHttpException(__('error.barobill.setting_not_configured'));
        }

        if (! $taxInvoice->canCancel()) {
            throw new BadRequestHttpException(__('error.barobill.cannot_cancel'));
        }

        try {
            // 세금계산서 취소 API 호출
            $response = $this->callApi('CancelTaxInvoice', [
                'CERTKEY' => $setting->cert_key,
                'CorpNum' => $setting->corp_num,
                'ID' => $setting->barobill_id,
                'InvoiceID' => $taxInvoice->barobill_invoice_id,
                'Memo' => $reason,
            ]);

            if ($response['Result'] === 0 || ! empty($response['Success'])) {
                $taxInvoice->status = TaxInvoice::STATUS_CANCELLED;
                $taxInvoice->cancelled_at = now();
                $taxInvoice->description = ($taxInvoice->description ? $taxInvoice->description."\n" : '').'취소 사유: '.$reason;
                $taxInvoice->save();

                Log::info('세금계산서 취소 성공', [
                    'tenant_id' => $this->tenantId(),
                    'tax_invoice_id' => $taxInvoice->id,
                    'reason' => $reason,
                ]);

                return $taxInvoice->fresh();
            }

            throw new \Exception($response['Message'] ?? '취소 실패');
        } catch (\Exception $e) {
            Log::error('세금계산서 취소 실패', [
                'tenant_id' => $this->tenantId(),
                'tax_invoice_id' => $taxInvoice->id,
                'error' => $e->getMessage(),
            ]);

            throw new BadRequestHttpException(__('error.barobill.cancel_failed').': '.$e->getMessage());
        }
    }

    /**
     * 국세청 전송 상태 조회
     */
    public function checkNtsSendStatus(TaxInvoice $taxInvoice): TaxInvoice
    {
        $setting = $this->getSetting();

        if (! $setting || ! $setting->canConnect()) {
            throw new BadRequestHttpException(__('error.barobill.setting_not_configured'));
        }

        if (empty($taxInvoice->barobill_invoice_id)) {
            throw new BadRequestHttpException(__('error.barobill.not_issued'));
        }

        try {
            $response = $this->callApi('GetTaxInvoiceState', [
                'CERTKEY' => $setting->cert_key,
                'CorpNum' => $setting->corp_num,
                'ID' => $setting->barobill_id,
                'InvoiceID' => $taxInvoice->barobill_invoice_id,
            ]);

            if (! empty($response['State'])) {
                $taxInvoice->nts_send_status = $response['State'];

                // 국세청 전송 완료 시 상태 업데이트
                if ($response['State'] === '전송완료' && ! $taxInvoice->sent_at) {
                    $taxInvoice->status = TaxInvoice::STATUS_SENT;
                    $taxInvoice->sent_at = now();
                    $taxInvoice->nts_confirm_num = $response['NTSConfirmNum'] ?? $taxInvoice->nts_confirm_num;
                }

                $taxInvoice->save();
            }

            return $taxInvoice->fresh();
        } catch (\Exception $e) {
            Log::error('국세청 전송 상태 조회 실패', [
                'tenant_id' => $this->tenantId(),
                'tax_invoice_id' => $taxInvoice->id,
                'error' => $e->getMessage(),
            ]);

            throw new BadRequestHttpException(__('error.barobill.status_check_failed').': '.$e->getMessage());
        }
    }

    // =========================================================================
    // Private 메서드
    // =========================================================================

    /**
     * 바로빌 API 호출
     */
    private function callApi(string $method, array $data): array
    {
        $baseUrl = $this->testMode ? self::API_TEST_URL : self::API_BASE_URL;
        $url = $baseUrl.'/TI/'.$method;

        $response = Http::timeout(30)
            ->withHeaders([
                'Content-Type' => 'application/json',
            ])
            ->post($url, $data);

        if ($response->failed()) {
            throw new \Exception('API 호출 실패: '.$response->status());
        }

        return $response->json() ?? [];
    }

    /**
     * 세금계산서 발행용 데이터 구성
     */
    private function buildTaxInvoiceData(TaxInvoice $taxInvoice, BarobillSetting $setting): array
    {
        // 품목 데이터 구성
        $items = [];
        foreach ($taxInvoice->items ?? [] as $index => $item) {
            $items[] = [
                'PurchaseDT' => $taxInvoice->issue_date->format('Ymd'),
                'ItemName' => $item['name'] ?? '',
                'Spec' => $item['spec'] ?? '',
                'Qty' => $item['qty'] ?? 1,
                'UnitCost' => $item['unit_price'] ?? 0,
                'SupplyCost' => $item['supply_amt'] ?? 0,
                'Tax' => $item['tax_amt'] ?? 0,
                'Remark' => $item['remark'] ?? '',
            ];
        }

        // 품목이 없는 경우 기본 품목 추가
        if (empty($items)) {
            $items[] = [
                'PurchaseDT' => $taxInvoice->issue_date->format('Ymd'),
                'ItemName' => $taxInvoice->description ?? '품목',
                'Spec' => '',
                'Qty' => 1,
                'UnitCost' => (float) $taxInvoice->supply_amount,
                'SupplyCost' => (float) $taxInvoice->supply_amount,
                'Tax' => (float) $taxInvoice->tax_amount,
                'Remark' => '',
            ];
        }

        return [
            'CERTKEY' => $setting->cert_key,
            'CorpNum' => $setting->corp_num,
            'ID' => $setting->barobill_id,
            'TaxInvoice' => [
                'InvoiceType' => $this->mapInvoiceType($taxInvoice->invoice_type),
                'IssueType' => $this->mapIssueType($taxInvoice->issue_type),
                'TaxType' => '과세',
                'PurposeType' => '영수',
                'WriteDate' => $taxInvoice->issue_date->format('Ymd'),

                // 공급자 정보
                'InvoicerCorpNum' => $taxInvoice->supplier_corp_num,
                'InvoicerCorpName' => $taxInvoice->supplier_corp_name,
                'InvoicerCEOName' => $taxInvoice->supplier_ceo_name,
                'InvoicerAddr' => $taxInvoice->supplier_addr,
                'InvoicerBizType' => $taxInvoice->supplier_biz_type,
                'InvoicerBizClass' => $taxInvoice->supplier_biz_class,
                'InvoicerContactID' => $taxInvoice->supplier_contact_id,

                // 공급받는자 정보
                'InvoiceeCorpNum' => $taxInvoice->buyer_corp_num,
                'InvoiceeCorpName' => $taxInvoice->buyer_corp_name,
                'InvoiceeCEOName' => $taxInvoice->buyer_ceo_name,
                'InvoiceeAddr' => $taxInvoice->buyer_addr,
                'InvoiceeBizType' => $taxInvoice->buyer_biz_type,
                'InvoiceeBizClass' => $taxInvoice->buyer_biz_class,
                'InvoiceeContactID' => $taxInvoice->buyer_contact_id,

                // 금액 정보
                'SupplyCostTotal' => (int) $taxInvoice->supply_amount,
                'TaxTotal' => (int) $taxInvoice->tax_amount,
                'TotalAmount' => (int) $taxInvoice->total_amount,

                // 품목 정보
                'TaxInvoiceTradeLineItems' => $items,

                // 비고
                'Remark1' => $taxInvoice->description ?? '',
            ],
        ];
    }

    /**
     * 세금계산서 유형 매핑
     */
    private function mapInvoiceType(string $type): string
    {
        return match ($type) {
            TaxInvoice::TYPE_TAX_INVOICE => '세금계산서',
            TaxInvoice::TYPE_INVOICE => '계산서',
            TaxInvoice::TYPE_MODIFIED_TAX_INVOICE => '수정세금계산서',
            default => '세금계산서',
        };
    }

    /**
     * 발행 유형 매핑
     */
    private function mapIssueType(string $type): string
    {
        return match ($type) {
            TaxInvoice::ISSUE_TYPE_NORMAL => '정발행',
            TaxInvoice::ISSUE_TYPE_REVERSE => '역발행',
            TaxInvoice::ISSUE_TYPE_TRUSTEE => '위수탁',
            default => '정발행',
        };
    }
}
