package com.izipay.tradeizipay.ui

import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.Spinner
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.izipay.tradeizipay.utils.initCyberSource
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.izipay.izipay_pw_sdk.data.model.AppearenceControlsPaymentIzipay
import com.izipay.izipay_pw_sdk.data.model.AppearencePaymentIzipay
import com.izipay.izipay_pw_sdk.data.model.AppearenceVisualSettingsPaymentIzipay
import com.izipay.izipay_pw_sdk.data.model.BillingPaymentIzipay
import com.izipay.izipay_pw_sdk.data.model.CardSelectorPaymentIzipay
import com.izipay.izipay_pw_sdk.data.model.ConfigRequest
import com.izipay.izipay_pw_sdk.data.model.CustomFields
import com.izipay.izipay_pw_sdk.data.model.CustomThemePaymentIzipay
import com.izipay.izipay_pw_sdk.data.model.CyberSource
import com.izipay.izipay_pw_sdk.data.model.OrderPaymentIzipay
import com.izipay.izipay_pw_sdk.data.model.PayOption
import com.izipay.izipay_pw_sdk.data.model.ShippingPaymentIzipay
import com.izipay.izipay_pw_sdk.data.model.TokenPaymentIzipay
import com.izipay.izipay_pw_sdk.domain.model.PaymentResponse
import com.izipay.izipay_pw_sdk.eventbus.EventBus
import com.izipay.izipay_pw_sdk.ui.embedded.callback.PayCallback
import com.izipay.izipay_pw_sdk.ui.embedded.callback.PayManager
import com.izipay.izipay_pw_sdk.ui.embedded.container.PaymentsFragment
import com.izipay.izipay_pw_sdk.ui.embedded.container.PaymentsObject
import com.izipay.izipay_pw_sdk.ui.fullscreend.ContainerActivity
import com.izipay.izipay_pw_sdk.utils.Response
import com.izipay.tradeizipay.R
import com.izipay.tradeizipay.databinding.FragmentHomeBinding
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

/**
 * Fragment desde el cual se llamará al SDK Móvil de Izipay
 * Información basada en https://developers.izipay.pe/android-core/integration-with-android/recommended-integration/
 * Actualizado al 05-11-2025
 */
@AndroidEntryPoint


/**
 * Integración con Android - Paso 6.2
 * https://developers.izipay.pe/android-core/integration-with-android/recommended-integration/#62-modo-embebido
 * Actualizado al 05-11-2025
 */
class HomeFragment : Fragment(), PayCallback {

    // region Properties

    private lateinit var binding: FragmentHomeBinding
    private lateinit var libraryManager: PayManager
    private lateinit var listaEscenario: Array<String>
    private lateinit var listPayMethod: ArrayList<PayOption>
    private var timeStamp = ""

    // endregion

    // region Lifecycle

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        listaEscenario = resources.getStringArray(R.array.escenario)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        binding = FragmentHomeBinding.inflate(inflater, container, false)

        setupToolbarTitle()
        setupSpinner()
        setupPayManager()
        setupPayButton()
        setupCyberSourceListener()

        return binding.root
    }

    // endregion

    // region Setup

    private fun setupToolbarTitle() {
        (requireActivity() as? AppCompatActivity)
            ?.supportActionBar?.title = "Cliente Demo Ktx"
    }

    private fun setupSpinner() {
        adapterSpinner(binding.spWindow, listaEscenario)
    }

    /**
     * Integración con Android - Paso 5.2
     */
    private fun setupPayManager() {
        libraryManager = PayManager()
        libraryManager.setCallback(this)
    }

    private fun setupPayButton() {
        binding.btnPagar.setOnClickListener {
            if (binding.tvTransactionID.text.toString().isEmpty())
                Toast.makeText(requireContext(), "Ingrese el número de transacción", Toast.LENGTH_SHORT).show()
            else
                intentIntegration()
        }
    }

    /**
     * Integración con Cybersource - Paso 3
     * https://developers.izipay.pe/android-core/integration-with-cybersource/
     * Actualizado al 05-11-2025
     */
    private fun setupCyberSourceListener() {
        lifecycleScope.launch {
            EventBus.subscribe<CyberSource> { cyber ->
                initCyberSource(
                    requireContext(),
                    cyber.guid!!,
                    cyber.url!!,
                    cyber.userOrg!!,
                    cyber.userScoring!!
                )
            }
        }
    }

    // endregion

    // region Integration

    /**
     * Integración con Android - Paso 6
     * https://developers.izipay.pe/android-core/integration-with-android/recommended-integration/#paso-6-agregar-archivo-hiltandroidapp
     * Actualizado al 05-11-2025
     */
    private fun buildConfigRequest(listPayMethod: ArrayList<PayOption>): ConfigRequest {
        /**
         * Revisar https://developers.izipay.pe/android-core/integration/parameters/
         * Para ver restricciones de formatos y longitudes en los campos
         */
        val orderNumber     = binding.tvTransactionID.text.toString()
        val transactionId   = binding.tvTransactionID.text.toString()
        timeStamp           = binding.tvTransactionID.text.toString()
        val merchantBuyerID = binding.tvMerchanBuyerID.text.toString()
            .ifEmpty { "MB${System.currentTimeMillis()}" }

        val claveHash = binding.tvClaveHash.text.toString()
        val action    = if (claveHash.isNotEmpty()) "pay_card_selector" else "pay_register"

        val keyWallet = if (action == "pay_card_selector") {
            val key = transactionId + transactionId + "3.00" + "PEN" + merchantBuyerID + "DNI" + "55555555"
            generateHMACBase64(key, claveHash)
        } else ""

        /**
         * A partir de la version 2.3.0 se requiere el envio del token de sesion al igual que lo hace el boton web de izipay.
         * el token debe ser generado en los servidores y enviado a la app, la cual posteriormente debe enviarselo al SDK para levantar el formulario
         * https://developers.izipay.pe/android-core/diagram/
         * Actualizado al 05-11-2025
         */
        val request = ConfigRequest(
            environment    = "QUAS",
            authorization  = binding.tvToken.text.toString(),
            action         = action,
            transactionId  = transactionId,
            merchantCode   = binding.spComercio.text.toString(),
            facilitatorCode = "",
            order = OrderPaymentIzipay(
                orderNumber          = orderNumber,
                currency             = "PEN",
                amount               = "3.00",
                payMethod            = listPayMethod,
                processType          = "autorize",
                merchantBuyerId      = merchantBuyerID,
                dateTimeTransaction  = transactionId
            ),
            token = TokenPaymentIzipay(
                cardToken = ""
            ),
            billing = BillingPaymentIzipay(
                firstName    = "Junior",
                lastName     = "Quispe",
                email        = "ejemplo01@gmail.com",
                phoneNumber  = "930292619",
                street       = "Av. flores",
                city         = "Lima",
                state        = "Lima",
                country      = "PE",
                postalCode   = "33065",
                documentType = "DNI",
                document     = "55555555"
            ),
            shipping = ShippingPaymentIzipay(
                firstName    = "Junior",
                lastName     = "Quispe",
                email        = "ejemplo01@gmail.com",
                phoneNumber  = "930292619",
                street       = "Av. Alamo",
                city         = "lima",
                state        = "lima",
                country      = "PE",
                postalCode   = "00001",
                documentType = "DNI",
                document     = "12345678"
            ),
            appearance = AppearencePaymentIzipay(
                language     = "ESP",
                formControls = AppearenceControlsPaymentIzipay(
                    isAmountLabelVisible  = true,
                    isLangControlVisible  = true
                ),
                visualSettings = AppearenceVisualSettingsPaymentIzipay(
                    showmessageResult = true
                ),
                theme       = "red",
                customTheme = CustomThemePaymentIzipay(
                    payButtonBackgroundColor      = "#333399",
                    textInputBorderColor          = "#333399",
                    textInputPlaceholderTextColor = "#333399"
                ),
                logo = ""
            ),
            urlIPN       = "https://oalanya.com/izipay/ipn/",
            cardSelector = if (action == "pay_card_selector") CardSelectorPaymentIzipay(keyWallet) else null,
            customFields = arrayListOf()
        )

        /**
         * En caso de requerir usar el flujo de card selector
         * se requiere un signature compuesto por los un grupo de campos (descritos en la seccion Definicion de Parametros de Izipay Developers https://developers.izipay.pe/android-core/integration/parameters/)
         * https://developers.izipay.pe/android-core/use-cases/pay_card_selector/
         * El signature debe ser creado en el servidor para no exponer la clave Hash dentro de la aplicacion
         * em este ejemplo la clave hash es usada en la app pero considerar que solo es para fines demostrativos
         */
        request.customFields?.add(CustomFields(code = "field1", value = "pruebacf"))

        return request
    }

    private fun intentIntegration() {
        /**
         * Métodos soportados al 20-11-2024 : Card y Yape
         */
        listPayMethod = arrayListOf<PayOption>().apply {
            if (binding.checkTarjeta.isChecked) add(PayOption.CARD)
            if (binding.checkYape.isChecked)    add(PayOption.YAPE)
        }

        val request = buildConfigRequest(listPayMethod)

        if (listaEscenario[binding.spWindow.selectedItemPosition] == "FULLSCREEN") {
            launchFullscreen(request)
        } else {
            /**
             * Integración con Android - Paso 5.2
             */
            launchEmbedded(request)
        }
    }

    private fun launchFullscreen(request: ConfigRequest) {
        val intent = Intent(activity, ContainerActivity::class.java)
            .putExtra(Response.REQUEST, request)
        responseLauncher.launch(intent)
    }

    private fun launchEmbedded(request: ConfigRequest) {
        val bundle = Bundle().apply { putSerializable("data", request) }
        val fragment = (PaymentsObject.paymentsObject() as PaymentsFragment).apply {
            arguments  = bundle
            payManager = libraryManager
        }
        parentFragmentManager.beginTransaction()
            .addToBackStack("HomeFragment")
            .add(R.id.navContainer, fragment)
            .commit()
    }

    // endregion

    // region Callbacks

    /**
     * Integración con Android - Paso 6.1
     * https://developers.izipay.pe/android-core/integration-with-android/recommended-integration/#61-modo-fullscreen-screen
     * Actualizado al 05-11-2025
     * Recepción de respuesta en modo fullscreen
     */
    private val responseLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result ->
        val dataSignature = result.data?.getStringExtra(Response.RESPONSESIGNATURE).orEmpty()
        val dataPayLoad: PaymentResponse = Gson().fromJson(
            result.data?.getStringExtra(Response.RESPONSEPAYLOAD).orEmpty(),
            object : TypeToken<PaymentResponse>() {}.type
        )
        val success  = result.resultCode == AppCompatActivity.RESULT_OK
        val response = "Success: $success \n\nTra.ID: $timeStamp \n\nSignature: $dataSignature \n\nDataPayLoad: $dataPayLoad"
        binding.etResponse.setText(response)
    }

    /**
     * Integración con Android - Paso 5.2
     * Recepción de respuesta en modo embebido
     */
    override fun onDataFromPayments(success: Boolean, pdata: PaymentResponse?, psignature: String?) {
        val response = "Success: $success \n\nTra.ID: $timeStamp \n\nData: $pdata \n\nSignature: $psignature"
        binding.etResponse.setText(response)

        if (parentFragmentManager.backStackEntryCount > 0)
            parentFragmentManager.popBackStack()
    }

    // endregion

    // region Helpers

    fun adapterSpinner(spinner: Spinner, opcionesArray: Array<String>) {
        val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, opcionesArray)
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
        spinner.adapter = adapter
    }

    fun generateHMACBase64(message: String, secretKey: String): String {
        val hmacSha256 = "HmacSHA256"
        val keySpec    = SecretKeySpec(secretKey.toByteArray(Charsets.UTF_8), hmacSha256)
        val mac        = Mac.getInstance(hmacSha256).apply { init(keySpec) }
        return android.util.Base64.encodeToString(
            mac.doFinal(message.toByteArray(Charsets.UTF_8)),
            android.util.Base64.NO_WRAP
        )
    }

    // endregion
}