package com.nhnkcp.verifonepossample

import android.graphics.Rect
import android.os.Bundle
import android.util.Log
import androidx.activity.OnBackPressedCallback
import androidx.core.view.isVisible
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.lifecycleScope
import com.google.android.material.chip.Chip
import com.google.gson.Gson
import com.nhnkcp.verifone.library.model.PaymentResponse
import com.nhnkcp.verifonepossample.data.PaymentRequest
import com.nhnkcp.verifonepossample.databinding.ActivityMainBinding
import com.nhnkcp.verifonepossample.utils.StringUtils.ifNotBlank
import com.nhnkcp.verifonepossample.utils.ViewUtils.hideKeyboard
import com.nhnkcp.verifonepossample.utils.ViewUtils.setOnSingleClickListener
import com.nhnkcp.verifonepossample.utils.ViewUtils.showSnackBar
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kr.co.kcp.api.KCPFactory

class MainActivity : BaseActivity<ActivityMainBinding>(R.layout.activity_main) {
    // 결제요청 구분 chip
    private var selectedChip = MutableLiveData<Chip>()
    // 승인방식 (VAN / PG)
    private var selectedServiceType = MutableLiveData<String>()

    // 뒤로가기 이벤트 수신을 위한 콜백 객체
    private val onBackPressedCallback = object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() { onHandleBackPressed() }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        onBackPressedDispatcher.addCallback(this, onBackPressedCallback)

        setListener()
        observeServiceType()
        observeChipValue()
        displayByServiceType()
        displayByChip()
        setVisibleTaxRate()

        // 결제요청 정보 입력 시, 키보드 올라왔을 때 화면 제어
        binding.root.viewTreeObserver.addOnGlobalLayoutListener {
            val rect = Rect()
            binding.root.getWindowVisibleDisplayFrame(rect)
            val screenHeight = binding.root.height
            val keypadHeight = screenHeight - rect.bottom

            if (keypadHeight > screenHeight * 0.15) { // 키보드가 올라왔을 때
                binding.main.setPadding(0, 0, 0, keypadHeight)
            } else { // 키보드가 내려갔을 때
                binding.main.setPadding(0, 0, 0, 0)
            }
        }
    }

    fun onHandleBackPressed() {
        lifecycleScope.launch(Dispatchers.IO) {
            val kaa = KCPFactory.getAndroidInstance()
            kaa.KCPEscape()
        }
    }

    // chip 선택값 실시간 반영
    private fun observeChipValue() {
        binding.chipGroup.setOnCheckedStateChangeListener { group, checkedIds ->
            if (checkedIds.isNotEmpty()) {
                val selected = findViewById<Chip>(checkedIds[0]) // 첫 번째 선택된 칩 가져오기
                selectedChip.value = selected
                val selectedText = selected.text.toString()
                Log.d("ChipSelection", "선택된 값: $selectedText")
            }
        }
    }

    private fun observeServiceType() {
        binding.radioGroupServiceType.setOnCheckedChangeListener { group, checkedId ->
            when(checkedId) {
                R.id.radioVAN -> selectedServiceType.value = "VAN"
                R.id.radioPG -> selectedServiceType.value = "PG"
            }
        }
    }

    // 승인방식에 따라 전문종류코드 변경 및 결제요청구분 제어
    private fun displayByServiceType() {
        with(binding) {
            selectedServiceType.observe(this@MainActivity) {
                btnCashICAuth.isVisible = !isPG()
                btnCashICCancel.isVisible = !isPG()
                btnUnionAuth.isVisible = !isPG()
                btnUnionCancel.isVisible = !isPG()
            }
        }
    }

    // 선택한 칩에 따른 화면 실시간 변경
    private fun displayByChip() {
        with(binding) {
            val cancelList = listOf(btnCreditCancel, btnCashReceiptCancel, btnUnionCancel, btnEasyPayCancel, btnCashICCancel)
            val cashReceiptList = listOf(btnCashReceiptAuth, btnCashReceiptCancel)
            val emptyList = listOf(btnCheckStore, btnPrint, btnBarcodeReq)

            selectedChip.observe(this@MainActivity) {
                layoutPriceAmount.isVisible = it !in emptyList
                layoutOriginTrade.isVisible = it in cancelList // 원거래정보 입력란
                layoutCashReceipt.isVisible = it in cashReceiptList // 현금영수증 발급정보 입력란
            }
        }
    }

    /** 결제 요청 부분 */
    private fun setListener() {
        with(binding) {
            btnRequest.setOnSingleClickListener {
                when (selectedChip.value) {
                    btnPrint -> {
                        printInit()
                        printMsg()
                        printLine()
                        printCut()
                    } // 프린트 출력
                    btnCheckStore -> { checkStore() } // 가맹점 정보 확인
                    btnCreditAuth -> creditAuth("0100", false)  // 신용 승인
                    btnCreditCancel -> creditAuth("0420", false)  // 신용 취소
                    btnCashReceiptAuth -> cashAuth("0100")  // 현금영수증 승인
                    btnCashReceiptCancel -> cashAuth("0420")  // 현금영수증 취소
                    btnEasyPayAuth -> barcodeReq(true, "0100") // 간편결제 승인
                    btnEasyPayCancel -> barcodeReq(true, "0420")  // 간편결제 취소
                    /* PG 사용 시, 은련/현금IC 는 결제사에 문의해주세요 */
                    btnUnionAuth -> creditAuth("0100", true) // 은련 승인
                    btnUnionCancel -> creditAuth("0420", true) // 은련 취소
                    btnBarcodeReq -> barcodeReq(false) // 바코드 데이터 요청
                    else -> {
                        lifecycleScope.launch(Dispatchers.Main) {
                            showSnackBar(binding.root, "결제요청 구분을 선택해주세요")
                        }
                    }
                }

                binding.root.hideKeyboard()
            }
        }
    }

    // 요청 공통 헤더
    private fun prepareInMap(trCode: String, procCode: String): LinkedHashMap<String, String> {
        val inMap = linkedMapOf<String, String>()

        // KCP VAN Gateway
        inMap["con_mode"] = REAL_SERVER
        // 전문종류코드
        inMap["proc_code"] = procCode
        // 거래구분코드
        inMap["tr_code"] = trCode
        // 포스연동 단말기 IP
        inMap["con_info1"] = binding.etDeviceIp.text.toString()
        // 포스연동 단말기 PORT
        inMap["con_info2"] = binding.etDevicePort.text.toString()

        return inMap
    }

    // 결제 관련 요청 공통 (금액 등)
    private fun preparePayInMap(trCode: String, procCode: String): LinkedHashMap<String, String> {
        val inMap = prepareInMap(trCode, procCode)

        // 거래금액옵션 (Y: 포스 직점 계산, N: 단말기 자동 계산)
        inMap["amtopt"] = if (binding.radioTradeOpt.checkedRadioButtonId == R.id.radioCalcPos) "Y" else "N"
        // 세금계산정보 (거래금액옵션: Y일 경우 설정)
        if (binding.layoutTaxRate.isVisible) {
            inMap["tax_info"] = "${binding.etTaxRate.text}${getRateType()}"
        }
        // 복합과세여부
        inMap["cpx_flag"] = if (binding.radioTaxSetting.checkedRadioButtonId == R.id.radioDutyFree) "Y" else "N"
        // 거래 총금액
        inMap["tot_amt"] = binding.etTotalAmount.text.toString()

        if (trCode != "0420") {
            // 할부개월
            binding.etInstallment.text.toString().ifNotBlank { inMap["ins_mon"] = it }
            // 세금
            binding.etTax.text.toString().ifNotBlank { inMap["tax_amt"] = it }
            // 봉사료
            binding.etTip.text.toString().ifNotBlank { inMap["svc_amt"] = it }
            // 면세금액
            binding.etDutyFree.text.toString().ifNotBlank { inMap["duty_free"] = it }
        } else {
            // 원거래 정보
            binding.etApprovalNo.text.toString().ifNotBlank { inMap["app_no"] = it }
            binding.etTradeDate.text.toString().ifNotBlank { inMap["otx_dt"] = it }
            binding.etTransNo.text.toString().ifNotBlank {
                inMap["proc_code"] = if (isPG()) "E00025" else "E00024"
                inMap["trans_no"] = it
                inMap["ms_data"] = "${binding.etTransCode.text}${0x0F.toChar()}$it"
            }
        }

        return inMap
    }

    /** 가맹점 정보 확인 */
    private fun checkStore() {
        lifecycleScope.launch(Dispatchers.IO) {
            val kaa = KCPFactory.getAndroidInstance()
            val inMap = preparePayInMap("0100", "E00001")
            val outMap = linkedMapOf<String, String>()

            kaa.KCPCatQuery(inMap, outMap)
            response(inMap, outMap)
        }
    }

    /** 바코드 요청 */
    private fun barcodeReq(isPay: Boolean, trCode: String? = null) {
        lifecycleScope.launch(Dispatchers.IO) {
            val kaa = KCPFactory.getAndroidInstance()
            val inMap = preparePayInMap("0100", "E00023")
            val outMap = linkedMapOf<String, String>()

            kaa.KCPCatQuery(inMap, outMap)

            if (isPay) {
                if (outMap["ms_data"].isNullOrBlank() || trCode.isNullOrBlank()) {
                    showSnackBar(binding.root, "데이터 오류. 다시 시도해주세요")
                    return@launch
                }

                easyPayAuth(outMap["ms_data"]!!, trCode)
            } else {
                response(inMap, outMap)
            }
        }
    }

    /** 신용 승인/취소 */
    private fun creditAuth(trCode: String, isUnion: Boolean) {
        lifecycleScope.launch(Dispatchers.IO) {
            val kaa = KCPFactory.getAndroidInstance()
            val inMap = preparePayInMap(trCode, if (isPG()) "E00017" else "E00002")
            val outMap = linkedMapOf<String, String>()

            if (isUnion) {
                inMap["tx_type"] = "21"
            }

            kaa.KCPCatQuery(inMap, outMap)
            response(inMap, outMap)
        }
    }

    private fun easyPayAuth(msData: String, trCode: String) {
        lifecycleScope.launch(Dispatchers.IO) {
            val kaa = KCPFactory.getAndroidInstance()
            val inMap = preparePayInMap(trCode, if (isPG()) "E00017" else "E00002").apply {
                this["tx_type"] = "82"
                this["ms_data"] = msData
            }
            val outMap = linkedMapOf<String, String>()

            kaa.KCPCatQuery(inMap, outMap)
            response(inMap, outMap)
        }
    }

    private fun cashIcAuth(trCode: String) {
        lifecycleScope.launch(Dispatchers.IO) {
            val kaa = KCPFactory.getAndroidInstance()
            val inMap = preparePayInMap(trCode, "E00003")
            val outMap = linkedMapOf<String, String>()

            kaa.KCPCatQuery(inMap, outMap)
            response(inMap, outMap)
        }
    }

    private fun cashAuth(trCode: String) {
        lifecycleScope.launch(Dispatchers.IO) {
            val kaa = KCPFactory.getAndroidInstance()
            val inMap = preparePayInMap(trCode, if (isPG()) "E00018" else "E00005")
            val outMap = linkedMapOf<String, String>()

            inMap["pass_wd"] = getCashType() // 발급용도, 0: 소비자 1: 사업자

            // 발급정보 있을 경우
            binding.etPrivacy.text.toString().ifNotBlank {
                inMap["ms_data"] = it
                inMap["tx_type"] = "10"
            }

            kaa.KCPCatQuery(inMap, outMap)
            response(inMap, outMap)
        }
    }

    // 현금영수증 발급용도
    private fun getCashType(): String {
        return when (binding.radioGroupCashType.checkedRadioButtonId) {
            R.id.radioPersonal -> "0"
            R.id.radioBusi -> "1"
            R.id.radioSelf -> "2"
            else -> ""
        }
    }

    private fun printInit() {
        lifecycleScope.launch(Dispatchers.IO) {
            val kaa = KCPFactory.getAndroidInstance()
            val inMap = prepareInMap("0100", "P00000")
            val outMap = linkedMapOf<String, String>()

            kaa.KCPCatQuery(inMap, outMap)
            response(inMap, outMap)
        }
    }

    private fun printMsg() {
        lifecycleScope.launch(Dispatchers.IO) {
            val kaa = KCPFactory.getAndroidInstance()
            val inMap = prepareInMap("0100", "P00001")
            val outMap = linkedMapOf<String, String>()

            inMap["prt_msg"] = "프린트 메시지"
            kaa.KCPCatQuery(inMap, outMap)
            response(inMap, outMap)
        }
    }

    private fun printLine() {
        lifecycleScope.launch(Dispatchers.IO) {
            val kaa = KCPFactory.getAndroidInstance()
            val inMap = prepareInMap("0100", "P00005")
            val outMap = linkedMapOf<String, String>()

            inMap["prt_line"] = "3"
            kaa.KCPCatQuery(inMap, outMap)
            response(inMap, outMap)
        }
    }

    private fun printCut() {
        lifecycleScope.launch(Dispatchers.IO) {
            val kaa = KCPFactory.getAndroidInstance()
            val inMap = prepareInMap("0100", "P00006")
            val outMap = linkedMapOf<String, String>()


            kaa.KCPCatQuery(inMap, outMap)
            response(inMap, outMap)
        }
    }

    private fun isPG() = selectedServiceType.value == "PG"

    private suspend fun response(inMap: LinkedHashMap<String, String>?, outMap: LinkedHashMap<String, String>) {
        var resResult: String? = ""

        Log.e("adad", Gson().toJson(outMap))

        resResult = "응답코드	: " + outMap["res_cd"] + "\n"
        resResult += ("응답메시지1	: " + outMap["resmsg1"] + "\n")
        resResult += ("응답메시지2	: " + outMap["resmsg2"] + "\n")
        if (outMap.get("res_cd").toString() == "0000") {
            resResult += ("TERM_ID	: " + outMap["term_id"] + "\n")
            resResult += ("승인날짜	: " + outMap["tx_dt"] + "\n")
            resResult += ("거래고유번호	: " + outMap["tx_no"] + "\n")
            resResult += ("승인번호	: " + outMap["app_no"] + "\n")
            resResult += ("카드구분	: " + outMap["card_gbn"] + "\n")
            resResult += ("프린트코드	: " + outMap["prt_cd"] + "\n")
            resResult += ("매입사코드	: " + outMap["ac_code"] + "\n")
            resResult += ("매입사명	: " + outMap["ac_name"] + "\n")
            resResult += ("발급사코드	: " + outMap["cc_cd"] + "\n")
            resResult += ("발급사명	: " + outMap["cc_name"] + "\n")
            resResult += ("서명데이터	: " + outMap["sign_img"] + "\n")
            resResult += ("거래코드	: " + outMap["trans_org_cd"] + "\n")
            resResult += ("거래번호	: " + outMap["trans_no"] + "\n")
        }

        Log.e("SocketResponseData", resResult)
        showSnackBar(binding.root, resResult)

        inMap?.clear()
        outMap.clear()
        System.gc()
        System.gc()
        System.gc()
    }

    // 세금계산방식이 포스계산일 경우만 세율 관련 레이아웃 visible
    private fun setVisibleTaxRate() {
        binding.radioTradeOpt.setOnCheckedChangeListener { group, checkedId ->
            binding.layoutTaxRate.isVisible = checkedId == R.id.radioCalcPos
        }
    }

    // 세율 반올림/올림/버림
    private fun getRateType(): String {
        return when (binding.radioRateType.checkedRadioButtonId) {
            R.id.radioRound -> "0"
            R.id.radioCeil -> "1"
            R.id.radioFloor -> "2"
            else -> ""
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        SocketConnection.disconnect()
    }

    companion object {
        private const val TEST_SERVER = "210.122.176.156:19991"
        private const val REAL_SERVER = "210.122.176.156:9977"
    }
}

