<script setup lang="ts">
import { ref, shallowRef, computed, onMounted, onUnmounted } from 'vue';

import api from '../../modules/Api';
import { purchaseAction } from '../../modules/Tracking/events/actions/purchase';

import type { Props, Emits, PaymentSuccessData, PaymentRedirectData } from './types';
import type { CartResponse } from '../../modules/Api/types';

// `PaymentHosted` renderiza o formulário de pagamento via iframe isolado,
// servido pelo serviço `render.uappi`. Todos os dados sensíveis (número do cartão,
// CVV, etc.) trafegam diretamente entre o iframe e o gateway, sem passar pela loja.
const props = defineProps<Props>();

const emit = defineEmits<Emits>();

const iframeEl = ref<HTMLIFrameElement | null>(null);

const paymentToken = ref<string | null>(null);
const loadingToken = ref(true);
const tokenError = ref<string | null>(null);
const iframeHeight = ref<number | null>(null);
// Controla o skeleton: só sai quando o iframe termina de carregar e emite
// UAPPI_PAYMENT_READY, não apenas quando o token foi obtido.
const paymentReady = ref(false);

// Snapshot do carrinho anterior à compra, usado apenas para disparar o evento
// `purchase` do GTM quando o `UAPPI_PAYMENT_SUCCESS` chegar. É necessário buscar
// esse dado antes do pagamento porque, após o sucesso, o carrinho já foi
// consumido pelo `/cart/buy` (chamado internamente pelo iframe) e não reflete
// mais os itens/frete/descontos da compra que acabou de ser feita.
const cartSnapshot = shallowRef<CartResponse | null>(null);

const iframeSrc = computed(() => {
  if (!paymentToken.value) return '';

  const host = props.iframeHost
    ?? (import.meta.env.VITE_PAYMENT_IFRAME_HOST as string | undefined)
    ?? 'https://payments.uappi.store';

  const url = new URL(host);

  url.searchParams.set('token', paymentToken.value);

  if (props.ctaColor) url.searchParams.set('ctaColor', props.ctaColor.replace('#', ''));
  if (props.lang) url.searchParams.set('lang', props.lang);
  if (props.countryCode) url.searchParams.set('countryCode', props.countryCode);

  return url.toString();
});

async function requestPaymentToken(): Promise<string> {
  const response = await api.cart.paymentToken({ paymentOptionId: props.option.id });

  if (!response?.token) {
    console.error('[PaymentHosted] Token ausente na resposta:', response);
    throw new Error('UAPPI_PAYMENT_TOKEN_MISSING');
  }

  return response.token;
}

async function fetchPaymentToken(): Promise<void> {
  loadingToken.value = true;
  tokenError.value = null;
  paymentReady.value = false;

  try {
    paymentToken.value = await requestPaymentToken();
  } catch (err) {
    console.error('[PaymentHosted] Erro ao buscar token de pagamento:', err);
    tokenError.value = 'Não foi possível iniciar o formulário de pagamento.';
  } finally {
    loadingToken.value = false;
  }
}

/**
 * Chamado quando o iframe avisa (UAPPI_PAYMENT_TOKEN_EXPIRING) que o token atual
 * está perto de expirar. Busca um novo token e o injeta direto no formulário via
 * postMessage (UAPPI_UPDATE_PAYMENT_TOKEN), sem trocar o `src` do iframe — preserva
 * os dados que o usuário já preencheu no cartão.
 */
async function refreshPaymentToken(): Promise<void> {
  if (!iframeEl.value?.contentWindow) return;

  try {
    const token = await requestPaymentToken();
    const targetOrigin = new URL(iframeSrc.value).origin;

    iframeEl.value.contentWindow.postMessage({ type: 'UAPPI_UPDATE_PAYMENT_TOKEN', token }, targetOrigin);
  } catch (err) {
    console.error('[PaymentHosted] Erro ao renovar token de pagamento:', err);
  }
}

async function fetchCartSnapshot(): Promise<void> {
  try {
    cartSnapshot.value = await api.cart.listDetail();
  } catch (err) {
    console.error('[PaymentHosted] Erro ao buscar carrinho para tracking de compra:', err);
  }
}

/**
 * Só processa mensagens cuja origem seja a própria janela do iframe renderizado
 * por este componente, evitando que outra janela/frame na página finja ser o
 * render.uappi e injete eventos de pagamento falsos.
 */
function handleIframeMessage(event: MessageEvent): void {
  if (!iframeEl.value || event.source !== iframeEl.value.contentWindow) return;

  const { type, data, url, errorCode, message, height } = (event.data ?? {}) as {
    type: string;
    data: PaymentSuccessData & PaymentRedirectData;
    url: string;
    errorCode: string | undefined;
    message: string;
    height: number | undefined;
  };

  switch (type) {
    case 'UAPPI_PAYMENT_READY':
      paymentReady.value = true;
      emit('payment-ready');
      break;

    case 'UAPPI_PAYMENT_TOKEN_EXPIRING':
      refreshPaymentToken();
      break;

    case 'UAPPI_PAYMENT_SUCCESS':
      if (cartSnapshot.value) {
        purchaseAction(props.option.paymentType.code, cartSnapshot.value);
      } else {
        console.error('[PaymentHosted] Carrinho indisponível: evento de tracking de compra (purchase) não foi disparado.');
      }
      emit('payment-success', data);
      break;

    case 'UAPPI_PAYMENT_REDIRECT':
      emit('payment-redirect', url, data);
      break;

    case 'UAPPI_PAYMENT_ERROR':
      emit('payment-error', errorCode, message);
      break;

    case 'UAPPI_PAYMENT_RESIZE':
      if (typeof height === 'number' && height > 0) iframeHeight.value = height;
      break;
  }
}

onMounted(async () => {
  window.addEventListener('message', handleIframeMessage);
  await Promise.all([fetchPaymentToken(), fetchCartSnapshot()]);
});

onUnmounted(() => {
  window.removeEventListener('message', handleIframeMessage);
});

</script>

<template>
  <div class="payment-hosted">
    <p v-if="tokenError" class="payment-hosted__error">
      {{ tokenError }}
    </p>

    <div v-else class="payment-hosted__stage">
      <div v-if="!paymentReady" class="payment-hosted__skeleton" />

      <iframe
        v-if="iframeSrc"
        ref="iframeEl"
        :src="iframeSrc"
        :style="iframeHeight ? { height: `${iframeHeight}px` } : undefined"
        :class="['payment-hosted__iframe', { 'payment-hosted__iframe--hidden': !paymentReady }]"
        frameborder="0"
        scrolling="no"
        title="Formulário de pagamento seguro"
        allow="payment"
      />
    </div>
  </div>
</template>

<style scoped src="./styles.scoped.css" />
