import { Button } from '../../../components/ui/button'
import { usePaymentNavigation } from '../lib/navigation'
import React from 'react'
import { PaymentInstructions } from '../components/PaymentInstructions'
import { GoPayLogosRow } from '../components/PaymentLogos'
import { postCharge } from '../../../services/api'
import { InlinePaymentStatus } from '../components/InlinePaymentStatus'
import { toast } from '../../../components/ui/toast'
import { LoadingOverlay } from '../../../components/LoadingOverlay'
import { Alert } from '../../../components/alert/Alert'
import { getErrorRecoveryAction, mapErrorToUserMessage } from '../../../lib/errorMessages'

// Global guards/tasks to stabilize QR generation across StrictMode remounts
const attemptedChargeKeys = new Set<string>()
const chargeTasks = new Map<string, Promise<any>>()

function sanitizeUrl(u?: string) {
  return (u || '').replace(/[`\s]+$/g, '').replace(/^\s+|\s+$/g, '').replace(/`/g, '')
}

function pickQrImageUrl(res: any, acts: Array<{ name?: string; method?: string; url: string }>) {
  const byName = acts.find((a) => /qr/i.test(a.name ?? '') || /qr-code/i.test(a.name ?? ''))
  const candidateFromActions = sanitizeUrl(byName?.url)
  if (candidateFromActions) return candidateFromActions
  const imageUrl = typeof res?.image_url === 'string' ? sanitizeUrl(res.image_url) : (typeof res?.qr_url === 'string' ? sanitizeUrl(res.qr_url) : '')
  if (imageUrl) return imageUrl
  const qrString = typeof res?.qr_string === 'string' ? res.qr_string : ''
  if (qrString) return `https://api.qrserver.com/v1/create-qr-code/?size=220x220&data=${encodeURIComponent(qrString)}`
  return ''
}

//

export function GoPayPanel({ orderId, amount, locked, onChargeInitiated }: { orderId: string; amount: number; locked?: boolean; onChargeInitiated?: () => void }) {
  const nav = usePaymentNavigation()
  const [busy, setBusy] = React.useState(false)
  const [qrUrl, setQrUrl] = React.useState<string>('')
  const [actions, setActions] = React.useState<Array<{ name?: string; method?: string; url: string }>>([])
  const [mode, setMode] = React.useState<'gopay' | 'qris'>('qris')
  const lastChargeKeyRef = React.useRef<string>('')
  const chargingKeyRef = React.useRef<string>('')
  const [errorMessage, setErrorMessage] = React.useState('')
  const [recovery, setRecovery] = React.useState<'retry' | 'view-existing' | 'back'>('retry')
  function openGoPay() {
    const deeplink = actions.find((a) => (a.name ?? '').toLowerCase().includes('deeplink'))
    window.open(deeplink?.url || 'https://www.gojek.com/gopay/', '_blank')
  }
  function downloadQR() {
    if (qrUrl) {
      const a = document.createElement('a')
      a.href = qrUrl
      a.download = `QR-${orderId}.png`
      a.target = '_blank'
      a.click()
    } else {
      toast.error('QR belum tersedia. Klik "Buat QR" terlebih dulu.')
    }
  }
  return (
    <>
      <div className="space-y-3">
        <LoadingOverlay isLoading={busy} message="Sedang membuat kode QR..." />
        <GoPayPanel_AutoEffect
          orderId={orderId}
          amount={amount}
          locked={locked}
          mode={mode}
          setBusy={setBusy}
          setQrUrl={setQrUrl}
          setActions={setActions}
          onChargeInitiated={onChargeInitiated}
          lastChargeKeyRef={lastChargeKeyRef}
          chargingKeyRef={chargingKeyRef}
          setErrorMessage={setErrorMessage}
          setRecovery={setRecovery}
        />
        <div className="font-medium">GoPay / QRIS</div>
        {errorMessage && (
          <Alert title="Informasi">
            {errorMessage}
            {recovery === 'view-existing' && (
              <div className="mt-2">
                <Button variant="outline" onClick={() => nav.toStatus(orderId, mode)}>Lihat Kode Pembayaran</Button>
              </div>
            )}
          </Alert>
        )}
        <GoPayLogosRow compact />
        <div className="flex items-center gap-2 text-xs">
          <span className="text-black/60">Mode:</span>
          <div className="inline-flex rounded-md border-2 border-black/20 overflow-hidden" role="group" aria-label="Pilih mode pembayaran">
            <button
              type="button"
              onClick={() => setMode('gopay')}
              aria-pressed={mode === 'gopay'}
              className={`px-2 py-1 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#2563EB] focus-visible:ring-offset-2 focus-visible:ring-offset-white transition ${mode === 'gopay' ? 'bg-black text-white' : 'bg-white text-black hover:bg-black/10'}`}
            >
              GoPay
            </button>
            <button
              type="button"
              onClick={() => setMode('qris')}
              aria-pressed={mode === 'qris'}
              className={`px-2 py-1 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#2563EB] focus-visible:ring-offset-2 focus-visible:ring-offset-white transition ${mode === 'qris' ? 'bg-black text-white' : 'bg-white text-black hover:bg-black/10'}`}
            >
              QRIS
            </button>
          </div>
        </div>
        <div className="rounded border border-black/10 p-3 flex flex-col items-center gap-2">
          <div className="text-xs text-black/60">Scan QR berikut menggunakan aplikasi {mode === 'gopay' ? 'GoPay' : 'QRIS'}</div>
          <div className="relative w-full max-w-[280px] aspect-square grid place-items-center rounded-md border border-black/20 bg-white">
            {mode === 'qris' && (!qrUrl || busy) ? (
              <span className="inline-flex items-center justify-center gap-2 text-xs text-black/60" role="status" aria-live="polite">
                <span className="h-4 w-4 animate-spin rounded-full border-2 border-black/40 border-t-transparent" aria-hidden />
                Membuat QR…
              </span>
            ) : qrUrl ? (
              <img src={qrUrl} alt="QR untuk pembayaran" className="aspect-square w-full max-w-[260px] mx-auto" />
            ) : null}
          </div>
          <div className="text-[10px] text-black/50">Mode: {mode.toUpperCase()}</div>
        </div>
        <div className="flex flex-col sm:flex-row gap-2 w-full">
          <Button variant="outline" className="w-full sm:w-auto" onClick={downloadQR} disabled={!qrUrl}>Download QR</Button>
          <Button variant="outline" className="w-full sm:w-auto" onClick={openGoPay} disabled={mode === 'qris'}>Buka GoPay</Button>
        </div>
        <div className="pt-2">
          <PaymentInstructions
            title={`Instruksi ${mode === 'gopay' ? 'GoPay' : 'QRIS'}`}
            steps={mode === 'gopay'
              ? [
                'Buka aplikasi GoPay dan pilih menu Scan.',
                'Arahkan kamera ke QR di layar.',
                'Periksa detail dan konfirmasi pembayaran di aplikasi.',
                'Simpan bukti pembayaran; status akan diperbarui otomatis.'
              ]
              : [
                'Buka aplikasi e-wallet/Bank yang mendukung QRIS (GoPay, ShopeePay, dll).',
                'Pilih menu Scan, arahkan kamera ke QR di layar.',
                'Periksa detail dan konfirmasi pembayaran di aplikasi.',
                'Simpan bukti pembayaran; status akan diperbarui otomatis.'
              ]}
          />
        </div>
        {locked && (
          <div className="text-xs text-black/60">Metode terkunci. Gunakan QR/deeplink untuk menyelesaikan pembayaran.</div>
        )}
        <div className="pt-2">
          <InlinePaymentStatus orderId={orderId} method={mode} />
          <div className="mt-2">
            <Button
              variant="outline"
              className="w-full"
              aria-busy={busy}
              disabled={busy || (!locked && actions.length === 0 && recovery !== 'view-existing')}
              onClick={() => { setBusy(true); onChargeInitiated?.(); setTimeout(() => { nav.toStatus(orderId, mode); setBusy(false) }, 250) }}
            >
              {busy ? (
                <span className="inline-flex items-center justify-center gap-2" role="status" aria-live="polite">
                  <span className="h-4 w-4 animate-spin rounded-full border-2 border-black/40 border-t-transparent" aria-hidden />
                  Menuju status…
                </span>
              ) : 'Buka halaman status'}
            </Button>
          </div>
        </div>
      </div>
    </>
  )
}

// Auto-generate QR for QRIS when mode is set to 'qris'
// Use effect to trigger charge once per order+mode, with guards for StrictMode/HMR
export function GoPayPanel_AutoEffect({ orderId, amount, locked, mode, setBusy, setQrUrl, setActions, onChargeInitiated, lastChargeKeyRef, chargingKeyRef, setErrorMessage, setRecovery }:
  { orderId: string; amount: number; locked?: boolean; mode: 'gopay' | 'qris'; setBusy: (b: boolean) => void; setQrUrl: (u: string) => void; setActions: (a: Array<{ name?: string; method?: string; url: string }>) => void; onChargeInitiated?: () => void; lastChargeKeyRef: React.MutableRefObject<string>; chargingKeyRef: React.MutableRefObject<string>; setErrorMessage: (m: string) => void; setRecovery: (a: 'retry' | 'view-existing' | 'back') => void }) {
  React.useEffect(() => {
    let cancelled = false
    async function run() {
      if (mode !== 'qris' || locked) return
      const chargeKey = `${orderId}:qris`
      // If a QRIS charge is already in-flight (StrictMode remount), await the shared task
      if (attemptedChargeKeys.has(chargeKey) && chargeTasks.has(chargeKey)) {
        setBusy(true)
        chargingKeyRef.current = chargeKey
        try {
          const res = await chargeTasks.get(chargeKey)!
          const acts: Array<{ name?: string; method?: string; url: string }> = Array.isArray(res?.actions) ? res.actions : []
          if (!cancelled) {
            setActions(acts)
            const url = pickQrImageUrl(res, acts)
            if (url) setQrUrl(url)
            try {
              const raw = localStorage.getItem('qrisCache')
              const map = raw ? (JSON.parse(raw) as Record<string, { url?: string; actions?: Array<{ name?: string; method?: string; url: string }> }>) : {}
              map[orderId] = { url, actions: acts }
              localStorage.setItem('qrisCache', JSON.stringify(map))
            } catch { void 0 }
            lastChargeKeyRef.current = chargeKey
            if (chargingKeyRef.current === chargeKey) chargingKeyRef.current = ''
            onChargeInitiated?.()
          }
        } catch (e) {
          if (!cancelled) {
            const msg = mapErrorToUserMessage(e)
            const act = getErrorRecoveryAction(e)
            setErrorMessage(msg)
            setRecovery(act)
            toast.error(msg)
          }
        } finally {
          if (!cancelled) {
            setBusy(false)
            if (chargingKeyRef.current === chargeKey) chargingKeyRef.current = ''
          }
          chargeTasks.delete(chargeKey)
        }
        return
      }
      if (lastChargeKeyRef.current === chargeKey || chargingKeyRef.current === chargeKey) return
      setBusy(true)
      chargingKeyRef.current = chargeKey
      try {
        const payload = { payment_type: 'qris', transaction_details: { order_id: orderId, gross_amount: amount }, qris: {} }
        attemptedChargeKeys.add(chargeKey)
        const task = postCharge(payload)
        chargeTasks.set(chargeKey, task)
        const res = await task
        const acts: Array<{ name?: string; method?: string; url: string }> = Array.isArray(res?.actions) ? res.actions : []
        if (!cancelled) {
          setActions(acts)
          const url = pickQrImageUrl(res, acts)
          if (url) setQrUrl(url)
          try {
            const raw = localStorage.getItem('qrisCache')
            const map = raw ? (JSON.parse(raw) as Record<string, { url?: string; actions?: Array<{ name?: string; method?: string; url: string }> }>) : {}
            map[orderId] = { url, actions: acts }
            localStorage.setItem('qrisCache', JSON.stringify(map))
          } catch { void 0 }
          lastChargeKeyRef.current = chargeKey
          if (chargingKeyRef.current === chargeKey) chargingKeyRef.current = ''
          onChargeInitiated?.()
        }
      } catch (e) {
        if (!cancelled) {
          const msg = mapErrorToUserMessage(e)
          const act = getErrorRecoveryAction(e)
          setErrorMessage(msg)
          setRecovery(act)
          toast.error(msg)
        }
        attemptedChargeKeys.delete(chargeKey)
      } finally {
        if (!cancelled) {
          setBusy(false)
          if (chargingKeyRef.current === chargeKey) chargingKeyRef.current = ''
        }
        chargeTasks.delete(chargeKey)
      }
    }
    run()
    return () => { cancelled = true }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [mode])
  return null
}