<?php

namespace App\Services\Barobill;

use App\Models\Barobill\BarobillMember;
use App\Models\Barobill\BarobillPricingPolicy;
use App\Models\Barobill\HometaxInvoice;
use Illuminate\Support\Facades\Log;

/**
 * 바로빌 사용량 집계 서비스
 *
 * 바로빌 API에는 직접적인 "사용량 집계" API가 없으므로,
 * 각 서비스별 내역 조회 API를 통해 건수를 집계합니다.
 *
 * 과금 정책 (DB 관리):
 * - 전자세금계산서: 기본 100건 무료, 추가 50건 단위 5,000원
 * - 계좌조회: 기본 1계좌 무료, 추가 1계좌당 10,000원
 * - 카드등록: 기본 5장 무료, 추가 1장당 5,000원
 */
class BarobillUsageService
{
    protected BarobillService $barobillService;

    public function __construct(BarobillService $barobillService)
    {
        $this->barobillService = $barobillService;
    }

    /**
     * 전체 테넌트 사용량 목록 조회
     *
     * @param  string  $startDate  시작일 (YYYYMMDD)
     * @param  string  $endDate  종료일 (YYYYMMDD)
     * @param  int|null  $tenantId  특정 테넌트만 조회 (null이면 전체)
     * @param  bool  $productionOnly  운영 모드만 조회 (기본값: true)
     */
    public function getUsageList(string $startDate, string $endDate, ?int $tenantId = null, bool $productionOnly = true): array
    {
        $query = BarobillMember::query()
            ->where('status', 'active')
            ->with('tenant:id,company_name');

        // 운영 모드만 조회 (테스트 모드 제외)
        if ($productionOnly) {
            $query->where('server_mode', 'production');
        }

        if ($tenantId) {
            $query->where('tenant_id', $tenantId);
        }

        $members = $query->get();
        $usageList = [];

        foreach ($members as $member) {
            $usage = $this->getMemberUsage($member, $startDate, $endDate);
            $usageList[] = $usage;
        }

        return $usageList;
    }

    /**
     * 단일 회원사 사용량 조회
     *
     * @param  string  $startDate  시작일 (YYYYMMDD)
     * @param  string  $endDate  종료일 (YYYYMMDD)
     */
    public function getMemberUsage(BarobillMember $member, string $startDate, string $endDate): array
    {
        // 해당 회원사의 서버 모드로 전환 (테스트/운영)
        $this->barobillService->setServerMode($member->server_mode ?? 'test');

        $taxInvoiceCount = $this->getTaxInvoiceCount($member, $startDate, $endDate);
        $bankAccountCount = $this->getBankAccountCount($member, $startDate, $endDate);
        $cardCount = $this->getCardCount($member, $startDate, $endDate);
        $hometaxCount = $this->getHometaxCount($member, $startDate, $endDate);

        // 전자세금계산서만 정책 기반 과금액 계산 (다른 서비스는 월정액)
        $taxInvoiceBilling = self::calculateBillingByPolicy('tax_invoice', $taxInvoiceCount);
        $taxInvoiceAmount = $taxInvoiceBilling['billable_amount'];

        return [
            'member_id' => $member->id,
            'tenant_id' => $member->tenant_id,
            'tenant_name' => $member->tenant?->company_name ?? '-',
            'biz_no' => $member->biz_no,
            'formatted_biz_no' => $member->formatted_biz_no,
            'corp_name' => $member->corp_name,
            'barobill_id' => $member->barobill_id,

            // 건수
            'tax_invoice_count' => $taxInvoiceCount,
            'bank_account_count' => $bankAccountCount,
            'card_count' => $cardCount,
            'hometax_count' => $hometaxCount,

            // 금액 (전자세금계산서만 건별 과금, 나머지는 월정액)
            'tax_invoice_amount' => $taxInvoiceAmount,
            'tax_invoice_billing' => $taxInvoiceBilling,
            'total_amount' => $taxInvoiceAmount,
        ];
    }

    /**
     * 통계 데이터 집계
     *
     * @param  array  $usageList  사용량 목록
     */
    public function aggregateStats(array $usageList): array
    {
        $totalMembers = count($usageList);
        $totalTaxInvoice = 0;
        $totalBankAccount = 0;
        $totalCard = 0;
        $totalHometax = 0;
        $totalAmount = 0;

        foreach ($usageList as $usage) {
            $totalTaxInvoice += $usage['tax_invoice_count'];
            $totalBankAccount += $usage['bank_account_count'];
            $totalCard += $usage['card_count'];
            $totalHometax += $usage['hometax_count'];
            $totalAmount += $usage['total_amount'];
        }

        return [
            'total_members' => $totalMembers,
            'total_tax_invoice' => $totalTaxInvoice,
            'total_bank_account' => $totalBankAccount,
            'total_card' => $totalCard,
            'total_hometax' => $totalHometax,
            'total_amount' => $totalAmount,
        ];
    }

    /**
     * 전자세금계산서 발행 건수 조회
     *
     * HometaxInvoice 테이블에서 해당 테넌트의 매출 세금계산서 건수를 카운트합니다.
     * (매출 = 발행한 세금계산서)
     */
    protected function getTaxInvoiceCount(BarobillMember $member, string $startDate, string $endDate): int
    {
        try {
            // YYYYMMDD -> YYYY-MM-DD 형식 변환
            $start = substr($startDate, 0, 4).'-'.substr($startDate, 4, 2).'-'.substr($startDate, 6, 2);
            $end = substr($endDate, 0, 4).'-'.substr($endDate, 4, 2).'-'.substr($endDate, 6, 2);

            return HometaxInvoice::where('tenant_id', $member->tenant_id)
                ->where('invoice_type', 'sales') // 매출 (발행한 세금계산서)
                ->whereBetween('write_date', [$start, $end])
                ->count();
        } catch (\Exception $e) {
            Log::warning('세금계산서 건수 조회 실패', [
                'member_id' => $member->id,
                'tenant_id' => $member->tenant_id,
                'error' => $e->getMessage(),
            ]);

            return 0;
        }
    }

    /**
     * 등록 계좌 수 조회
     *
     * 월정액 과금 기준: 등록된 계좌 개수
     */
    protected function getBankAccountCount(BarobillMember $member, string $startDate, string $endDate): int
    {
        try {
            $result = $this->barobillService->getBankAccounts($member->biz_no, true);

            Log::info('계좌 조회 API 응답', [
                'member_id' => $member->id,
                'biz_no' => $member->biz_no,
                'server_mode' => $member->server_mode,
                'success' => $result['success'] ?? false,
                'data_type' => isset($result['data']) ? gettype($result['data']) : 'null',
                'data' => $result['data'] ?? null,
            ]);

            if ($result['success'] && isset($result['data'])) {
                // 배열이면 count
                if (is_array($result['data'])) {
                    return count($result['data']);
                }
                // 객체에 계좌 목록이 있으면
                if (is_object($result['data'])) {
                    // GetBankAccountEx 응답: BankAccount 또는 BankAccountEx 배열
                    if (isset($result['data']->BankAccount)) {
                        $accounts = $result['data']->BankAccount;

                        return is_array($accounts) ? count($accounts) : 1;
                    }
                    if (isset($result['data']->BankAccountEx)) {
                        $accounts = $result['data']->BankAccountEx;

                        return is_array($accounts) ? count($accounts) : 1;
                    }
                }
            }
        } catch (\Exception $e) {
            Log::warning('등록 계좌 수 조회 실패', [
                'member_id' => $member->id,
                'error' => $e->getMessage(),
            ]);
        }

        return 0;
    }

    /**
     * 등록 카드 수 조회
     *
     * 월정액 과금 기준: 등록된 카드 개수
     */
    protected function getCardCount(BarobillMember $member, string $startDate, string $endDate): int
    {
        try {
            $result = $this->barobillService->getCards($member->biz_no, true);

            Log::info('카드 조회 API 응답', [
                'member_id' => $member->id,
                'biz_no' => $member->biz_no,
                'server_mode' => $member->server_mode,
                'success' => $result['success'] ?? false,
                'data_type' => isset($result['data']) ? gettype($result['data']) : 'null',
                'data' => $result['data'] ?? null,
            ]);

            if ($result['success'] && isset($result['data'])) {
                // 배열이면 count
                if (is_array($result['data'])) {
                    return count($result['data']);
                }
                // 객체에 카드 목록이 있으면
                if (is_object($result['data'])) {
                    // GetCardEx2 응답: CardEx 배열
                    if (isset($result['data']->CardEx)) {
                        $cards = $result['data']->CardEx;

                        return is_array($cards) ? count($cards) : 1;
                    }
                }
            }
        } catch (\Exception $e) {
            Log::warning('등록 카드 수 조회 실패', [
                'member_id' => $member->id,
                'error' => $e->getMessage(),
            ]);
        }

        return 0;
    }

    /**
     * 홈텍스 매입/매출 건수 조회
     *
     * HometaxInvoice 테이블에서 해당 테넌트의 전체 세금계산서 건수를 카운트합니다.
     * (매입 + 매출 모두 포함)
     */
    protected function getHometaxCount(BarobillMember $member, string $startDate, string $endDate): int
    {
        try {
            // YYYYMMDD -> YYYY-MM-DD 형식 변환
            $start = substr($startDate, 0, 4).'-'.substr($startDate, 4, 2).'-'.substr($startDate, 6, 2);
            $end = substr($endDate, 0, 4).'-'.substr($endDate, 4, 2).'-'.substr($endDate, 6, 2);

            return HometaxInvoice::where('tenant_id', $member->tenant_id)
                ->whereBetween('write_date', [$start, $end])
                ->count();
        } catch (\Exception $e) {
            Log::warning('홈텍스 건수 조회 실패', [
                'member_id' => $member->id,
                'tenant_id' => $member->tenant_id,
                'error' => $e->getMessage(),
            ]);

            return 0;
        }
    }

    /**
     * 서비스별 과금 정책 정보 반환 (DB에서 조회)
     */
    public static function getPriceInfo(): array
    {
        $policies = BarobillPricingPolicy::active()->orderBy('sort_order')->get();

        $priceInfo = [];

        foreach ($policies as $policy) {
            $priceInfo[$policy->service_type] = [
                'name' => $policy->name,
                'free_quota' => $policy->free_quota,
                'free_quota_unit' => $policy->free_quota_unit,
                'additional_unit' => $policy->additional_unit,
                'additional_unit_label' => $policy->additional_unit_label,
                'additional_price' => $policy->additional_price,
                'description' => $policy->policy_description,
            ];
        }

        // 기본값 설정 (DB에 정책이 없는 경우)
        $defaults = [
            'tax_invoice' => [
                'name' => '계산서 발행',
                'free_quota' => 100,
                'free_quota_unit' => '건',
                'additional_unit' => 50,
                'additional_unit_label' => '건',
                'additional_price' => 5000,
                'description' => '기본 100건 무료, 추가 50건 단위 5,000원',
            ],
            'bank_account' => [
                'name' => '계좌조회 수집',
                'free_quota' => 1,
                'free_quota_unit' => '개',
                'additional_unit' => 1,
                'additional_unit_label' => '계좌',
                'additional_price' => 10000,
                'description' => '기본 1계좌 무료, 추가 1계좌당 10,000원',
            ],
            'card' => [
                'name' => '법인카드 등록',
                'free_quota' => 5,
                'free_quota_unit' => '장',
                'additional_unit' => 1,
                'additional_unit_label' => '장',
                'additional_price' => 5000,
                'description' => '기본 5장 무료, 추가 1장당 5,000원',
            ],
            'hometax' => [
                'name' => '홈텍스 매입/매출',
                'free_quota' => 0,
                'free_quota_unit' => '건',
                'additional_unit' => 1,
                'additional_unit_label' => '건',
                'additional_price' => 0,
                'description' => '월정액 (별도 협의)',
            ],
        ];

        // 없는 정책은 기본값으로 채움
        foreach ($defaults as $type => $default) {
            if (! isset($priceInfo[$type])) {
                $priceInfo[$type] = $default;
            }
        }

        return $priceInfo;
    }

    /**
     * 사용량에 따른 과금액 계산
     *
     * @param  string  $serviceType  서비스 유형
     * @param  int  $usageCount  사용량/등록 수
     * @return array ['free_count' => int, 'billable_count' => int, 'billable_amount' => int]
     */
    public static function calculateBillingByPolicy(string $serviceType, int $usageCount): array
    {
        $policy = BarobillPricingPolicy::getByServiceType($serviceType);

        if (! $policy) {
            // 기본 정책이 없으면 과금 없음
            return [
                'free_count' => $usageCount,
                'billable_count' => 0,
                'billable_amount' => 0,
            ];
        }

        return $policy->calculateBilling($usageCount);
    }
}
