<?php

namespace App\Http\Traits;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cookie;
use App\Models\DTO\Toss\AuthData;
use App\Models\DTO\Toss\CancelData;

trait TossTrait
{
    use CommonTrait;

    /**
     * 토스 API Client Key
     */
    protected function getClientKey(): string
    {
        return (config('use_pg_test', 0) ? config('test_client_key') : config('live_client_key'));
    }

    /**
     * 토스 API Secret Key
     */
    protected function getSecretKey(): string
    {
        return (config('use_pg_test', 0) ? config('test_secret_key') : config('live_secret_key'));
    }

    /**
     * 토스 인증 일련번호
     */
    protected function getAuthorization(): string
    {
        return ('Basic ' . base64_encode($this->getSecretKey() . ':'));
    }

    /**
     * 토스 실 본인인증 사용 여부
     */
    protected function getTossCertIsTest(): bool
    {
        return config('toss_cert_is_test', 0);
    }

    /**
     * 토스 카드사 정보
     */
    protected function getCards(): array
    {
        return [
            '3K' => '기업비씨', '46' => '광주', '71' => '롯데', '30' => '산업',
            '31' => 'BC카드', '51' => '삼성카드', '38' => '새마을', '41' => '신한',
            '62' => '신협', '36' => '씨티', '33' => '우리', 'W1' => '우리',
            '37' => '우체국', '39' => '저축', '35' => '전북', '42' => '제주',
            '15' => '카카오뱅크', '3A' => '케이뱅크', '24' => '토스뱅크', '21' => '하나',
            '61' => '현대', '11' => '국민', '91' => '농협', '34' => '수협'
        ];
    }

    /**
     * 토스 은행/증권사 정보
     */
    protected function getBanks(): array
    {
        return [
            '39' => '경남은행', '34' => '광주은행', 'S8' => '교보증권', '12' => '단위농협(지역농축협)',
            'SE' => '대신증권', 'SK' => '메리츠증권', 'S5' => '미래에셋증권', 'SM' => '부국증권',
            '32' => '부산은행', 'S3' => '삼성증권', '45' => '새마을금고', '64' => '산림조합',
            'SN' => '신영증권', 'S2' => '신한금융투자', '88' => '신한은행', '48' => '신협',
            '27' => '씨티은행', '20' => '우리은행', '71' => '우체국예금보험', 'S0' => '유안타증권',
            'SJ' => '유진투자증권', '50' => '저축은행중앙회', '37' => '전북은행', '35' => '제주은행',
            '90' => '카카오뱅크', '288' => '카카오페이증권', '89' => '케이뱅크', '92' => '토스뱅크',
            'ST' => '토스증권', 'SR' => '펀드온라인코리아(한국포스증권)', 'SH' => '하나금융투자', '81' => '하나은행',
            'S9' => '하이투자증권', 'S6' => '한국투자증권', 'SG' => '한화투자증권', 'SA' => '현대차증권',
            '54' => '홍콩상하이은행', 'SI' => 'DB금융투자', '31' => 'DGB대구은행', '03' => 'IBK기업은행',
            '06' => 'KB국민은행', 'S4' => 'KB증권', '02' => 'KDB산업은행', 'SP' => 'KTB투자증권(다올투자증권)',
            'SO' => 'LIG투자증권', '11' => 'NH농협은행', 'SL' => 'NH투자증권', '23' => 'SC제일은행',
            '07' => 'Sh수협은행', 'SD' => 'SK증권'
        ];
    }

    /**
     * 카드 혜택 조회
     */
    protected function requestCardPromotions(): mixed
    {
        $response = Http::withHeaders([
            'Authorization' => $this->getAuthorization(),
        ])->get('https://api.tosspayments.com/v1/promotions/card');

        $res = $response->json();
        if (isset($res['interestFreeCards']) > 0) {
            $interestFreeCards = $res['interestFreeCards'];
            foreach ($interestFreeCards as $i => $row) {
                $res['interestFreeCards'][$i]['min'] = min($row['installmentFreeMonths']);
                $res['interestFreeCards'][$i]['max'] = max($row['installmentFreeMonths']);
                $res['interestFreeCards'][$i]['minimumPaymentAmountKor'] = $this->numberToHangul($row['minimumPaymentAmount']);
            }
        }

        return $res;
    }

    /**
     * 결제 승인 API 호출
     */
    protected function requestPayment(string $paymentKey, string $orderId, int $amount): mixed
    {
        // 기본 결제 요청
        $response = Http::withHeaders([
            'Authorization' => $this->getAuthorization(),
            'Content-Type' => 'application/json'
        ])->post('https://api.tosspayments.com/v1/payments/confirm', [
            'paymentKey' => $paymentKey,
            'orderId' => $orderId,
            'amount' => $amount
        ]);

        return $response->json();
    }

    /**
     * 결제 취소 API 호출
     */
    protected function requestCancel(string $paymentKey, string $idempotencyKey, CancelData $cancelData): mixed
    {
        // 기본 결제 요청
        $response = Http::withHeaders([
            'Authorization' => $this->getAuthorization(),
            'Content-Type' => 'application/json',
            'Idempotency-Key' => $idempotencyKey,
        ])->post(sprintf('https://api.tosspayments.com/v1/payments/%s/cancel', $paymentKey),
            $cancelData->toArray()
        );

        return $response->json();
    }

    /**
     * 본인확인 Access Token 요청
     */
    protected function requestAccessToken(Request $request): ?array
    {
        $tossCertIsTest = $this->getTossCertIsTest();
        if (!$request->hasCookie('tossAccessToken')) {
            $token = Http::asForm()->post('https://oauth2.cert.toss.im/token', [
                'grant_type' => 'client_credentials',
                'client_id' => ($tossCertIsTest ? config('test_toss_cert_client_id') : config('live_toss_cert_client_id')),
                'client_secret' => ($tossCertIsTest ? config('test_toss_cert_client_secret') : config('live_toss_cert_client_secret')),
                'scope' => 'ca'
            ])->json();
        } else {
            $token = unserialize($request->cookie('tossAccessToken'));
            if ($token['tossCertIsTest'] != $tossCertIsTest) {
                Cookie::expire('tossAccessToken');
                $token = $this->requestAccessToken($request);
            }
        }
        return ($token + ['tossCertIsTest' => $tossCertIsTest]);
    }

    /**
     * 본인확인 요청
     */
    protected function requestUserAuth(array $token, AuthData $authData): object|array|null
    {
        return Http::asJson()->withHeaders([
            'Authorization' => sprintf("%s %s", $token['token_type'], $token['access_token'])
        ])->post('https://cert.toss.im/api/v2/sign/user/auth/id/request', $authData->toArray())->object();
    }

    /**
     * 본인확인 상태 조회
     */
    protected function requestUserAuthStatus(array $token, string $txId): object|array|null
    {
        return Http::asJson()->withHeaders([
            'Authorization' => sprintf("%s %s", $token['token_type'], $token['access_token'])
        ])->post('https://cert.toss.im/api/v2/sign/user/auth/id/status', [
            'txId' => $txId,
        ])->object();
    }

    /**
     * 본인확인 결과 확인
     */
    protected function requestCertResult(array $token, string $txId, string $sessionKey): object|array|null
    {
        return Http::asJson()->withHeaders([
            'Authorization' => sprintf("%s %s", $token['token_type'], $token['access_token'])
        ])->post('https://cert.toss.im/api/v2/sign/user/auth/id/result', [
            'txId' => $txId,
            'sessionKey' => $sessionKey
        ])->object();
    }

    /**
     * 본인확인 결과 조회
     */
    protected function getTossCertResult(array $token, string $txId): ?object
    {
        $sessionId = $this->generateSessionId();
        $secretKey = $this->generateRandomBytes(32);
        $iv = $this->generateRandomBytes(12);
        $sessionKey = $this->generateSessionKey($sessionId, $secretKey, $iv);

        $result = $this->requestCertResult($token, $txId, $sessionKey);

        if ($result->resultType == 'FAIL') {
            return $result;
        }

        $personalData = $result->success->personalData;

        $result->success->personalData = (object)[
            'ci' => $this->decryptData($secretKey, $iv, $personalData->ci),
            'name' => $this->decryptData($secretKey, $iv, $personalData->name),
            'birthday' => $this->decryptData($secretKey, $iv, $personalData->birthday),
            'gender' => $this->decryptData($secretKey, $iv, $personalData->gender),
            'nationality' => $this->decryptData($secretKey, $iv, $personalData->nationality),
            'di' => $this->decryptData($secretKey, $iv, $personalData->di)
        ];

        return $result;
    }
}
