import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { checkOrder, getGoods, order, type GoodsItem } from '@/api/pay'
import { getService } from '@/api/user'

const PAY_METHOD_LIST = [
  { code: 'weixin', icon: '/images/pay/icon-wechatpay.png', name: '微信支付' },
  { code: 'alipay', icon: '/images/pay/icon-alipay.png', name: '支付宝' },
]

const ORDER_POLLING_INTERVAL = 2000
const ORDER_POLLING_MAX_COUNT = 60
const PENDING_PAYMENT_STORAGE_KEY = 'pending-payment-order'
const PENDING_PAYMENT_COOKIE_KEY = 'pending-payment-order'
const PENDING_PAYMENT_MAX_AGE = 15 * 60 * 1000

type PayMethodInfo = (typeof PAY_METHOD_LIST)[number]

type PendingPayment = {
  expiresAt: number
  orderId: string
}

function parsePendingPayment(value: string | null): PendingPayment | null {
  if (!value) {
    return null
  }

  try {
    const pendingPayment: unknown = JSON.parse(value)
    if (
      !pendingPayment ||
      typeof pendingPayment !== 'object' ||
      typeof (pendingPayment as PendingPayment).orderId !== 'string' ||
      typeof (pendingPayment as PendingPayment).expiresAt !== 'number' ||
      (pendingPayment as PendingPayment).expiresAt <= Date.now()
    ) {
      return null
    }

    return pendingPayment as PendingPayment
  } catch {
    return null
  }
}

function getPendingPaymentCookie() {
  const cookiePrefix = `${PENDING_PAYMENT_COOKIE_KEY}=`
  const cookie = document.cookie.split('; ').find((value) => value.startsWith(cookiePrefix))

  if (!cookie) {
    return null
  }

  try {
    return decodeURIComponent(cookie.slice(cookiePrefix.length))
  } catch {
    return null
  }
}

function getPendingPayment(): PendingPayment | null {
  let storedPendingPayment: PendingPayment | null = null

  try {
    storedPendingPayment = parsePendingPayment(window.localStorage.getItem(PENDING_PAYMENT_STORAGE_KEY))
  } catch {
    // Fall back to the cookie when localStorage is unavailable.
  }

  const pendingPayment = storedPendingPayment ?? parsePendingPayment(getPendingPaymentCookie())

  if (!pendingPayment) {
    clearPendingPayment()
  }

  return pendingPayment
}

function savePendingPayment(orderId: string) {
  const pendingPayment: PendingPayment = {
    orderId,
    expiresAt: Date.now() + PENDING_PAYMENT_MAX_AGE,
  }
  const serializedPendingPayment = JSON.stringify(pendingPayment)

  try {
    window.localStorage.setItem(PENDING_PAYMENT_STORAGE_KEY, serializedPendingPayment)
  } catch {
    // Cookie storage below still allows the return page to recover the order.
  }

  document.cookie = `${PENDING_PAYMENT_COOKIE_KEY}=${encodeURIComponent(serializedPendingPayment)}; Max-Age=${Math.ceil(
    PENDING_PAYMENT_MAX_AGE / 1000,
  )}; Path=/; SameSite=Lax${window.location.protocol === 'https:' ? '; Secure' : ''}`
}

function clearPendingPayment(orderId?: string) {
  let storedPendingPayment: PendingPayment | null = null

  try {
    storedPendingPayment = parsePendingPayment(window.localStorage.getItem(PENDING_PAYMENT_STORAGE_KEY))
  } catch {
    // Cookie cleanup below still runs when localStorage is unavailable.
  }

  const pendingPayment = storedPendingPayment ?? parsePendingPayment(getPendingPaymentCookie())
  if (!orderId || !pendingPayment || pendingPayment.orderId === orderId) {
    try {
      window.localStorage.removeItem(PENDING_PAYMENT_STORAGE_KEY)
    } catch {
      // Cookie cleanup below still runs when localStorage is unavailable.
    }

    document.cookie = `${PENDING_PAYMENT_COOKIE_KEY}=; Max-Age=0; Path=/; SameSite=Lax${
      window.location.protocol === 'https:' ? '; Secure' : ''
    }`
  }
}

function isIOSSafari() {
  const userAgent = navigator.userAgent
  const isIOS = /iPad|iPhone|iPod/.test(userAgent) ||
    (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)

  return isIOS && /Safari/.test(userAgent) && !/CriOS|FxiOS|EdgiOS|OPiOS/.test(userAgent)
}

function getWeixinH5PayUrl(h5Url: string) {
  const payUrl = new URL(h5Url)

  if (!payUrl.searchParams.has('redirect_url')) {
    payUrl.searchParams.set('redirect_url', window.location.href)
  }

  return payUrl.toString()
}

function getSupportedPayMethods(goods?: GoodsItem) {
  if (!goods?.pay_type) {
    return []
  }

  const supportedPayTypes = new Set(
    goods.pay_type
      .split(',')
      .map((payType) => payType.trim())
      .filter(Boolean),
  )

  return PAY_METHOD_LIST.filter((method) => supportedPayTypes.has(method.code))
}

function PayPage() {
  const navigate = useNavigate()
  const [goodsList, setGoodsList] = useState<GoodsItem[]>([])
  const [currentIndex, setCurrentIndex] = useState(0)
  const [selectedPayType, setSelectedPayType] = useState('')
  const [isPaying, setIsPaying] = useState(false)
  const [serviceUrl, setServiceUrl] = useState('')
  const pollingTimerRef = useRef<number | null>(null)
  const pollingCountRef = useRef(0)
  const pollingSessionRef = useRef(0)
  const paymentAttemptRef = useRef(0)
  const previousSelectionRef = useRef({ planId: '', payType: '' })
  const isMountedRef = useRef(true)
  const selectedPlan = goodsList[currentIndex]
  const selectedPlanId = selectedPlan?.goods_id ?? ''
  const supportedPayMethods = useMemo(() => getSupportedPayMethods(selectedPlan), [selectedPlan])

  const stopOrderPolling = useCallback(() => {
    pollingSessionRef.current += 1

    if (pollingTimerRef.current !== null) {
      window.clearTimeout(pollingTimerRef.current)
      pollingTimerRef.current = null
    }
    pollingCountRef.current = 0
  }, [])

  const cancelPaymentFlow = useCallback(() => {
    paymentAttemptRef.current += 1
    stopOrderPolling()
    clearPendingPayment()

    if (isMountedRef.current) {
      setIsPaying(false)
    }
  }, [stopOrderPolling])

  useEffect(() => {
    let isMounted = true

    async function initGoods() {
      try {
        const { data } = await getGoods()
        const nextGoodsList = Array.isArray(data) ? data : []
        const recommendIndex = nextGoodsList.findIndex((goods) => goods.recommend)
        const checkedIndex = nextGoodsList.findIndex((goods) => goods.checked)

        if (!isMounted) {
          return
        }

        setGoodsList(nextGoodsList)
        setCurrentIndex(recommendIndex >= 0 ? recommendIndex : checkedIndex >= 0 ? checkedIndex : 0)
      } catch {
        if (isMounted) {
          setGoodsList([])
          setCurrentIndex(0)
        }
      }
    }

    initGoods()

    return () => {
      isMounted = false
    }
  }, [])

  useEffect(() => {
    let isMounted = true

    async function initServiceUrl() {
      try {
        const { data } = await getService('permanent')
        const nextServiceUrl = data['kf.address']

        if (isMounted && typeof nextServiceUrl === 'string') {
          setServiceUrl(nextServiceUrl)
        }
      } catch {
        if (isMounted) {
          setServiceUrl('')
        }
      }
    }

    initServiceUrl()

    return () => {
      isMounted = false
    }
  }, [])

  useEffect(() => {
    isMountedRef.current = true

    return () => {
      isMountedRef.current = false
      stopOrderPolling()
    }
  }, [stopOrderPolling])

  useEffect(() => {
    setSelectedPayType((currentPayType) => {
      if (supportedPayMethods.some((method) => method.code === currentPayType)) {
        return currentPayType
      }

      return supportedPayMethods[0]?.code ?? ''
    })
  }, [supportedPayMethods])

  useEffect(() => {
    const previousSelection = previousSelectionRef.current
    const hasSelectionChanged =
      previousSelection.planId !== selectedPlanId || previousSelection.payType !== selectedPayType

    const hasCompleteSelection = Boolean(
      previousSelection.planId && previousSelection.payType && selectedPlanId && selectedPayType,
    )

    if (isPaying && hasCompleteSelection && hasSelectionChanged) {
      cancelPaymentFlow()
    }

    previousSelectionRef.current = {
      planId: selectedPlanId,
      payType: selectedPayType,
    }
  }, [cancelPaymentFlow, isPaying, selectedPayType, selectedPlanId])

  const startOrderPolling = useCallback(
    (orderId: string) => {
      stopOrderPolling()
      const sessionId = pollingSessionRef.current

      const pollOrder = async () => {
        if (pollingSessionRef.current !== sessionId) {
          return
        }

        pollingCountRef.current += 1

        try {
          const { data } = await checkOrder(orderId)

          if (pollingSessionRef.current !== sessionId) {
            return
          }

          if (data.status === '2') {
            stopOrderPolling()
            clearPendingPayment(orderId)
            navigate('/pay-result?success=true')
            return
          }
        } catch {
          if (pollingSessionRef.current !== sessionId) {
            return
          }

          stopOrderPolling()
          if (isMountedRef.current) {
            setIsPaying(false)
          }
          return
        }

        if (pollingCountRef.current >= ORDER_POLLING_MAX_COUNT) {
          stopOrderPolling()
          if (isMountedRef.current) {
            setIsPaying(false)
          }
          return
        }

        if (pollingSessionRef.current !== sessionId) {
          return
        }

        pollingTimerRef.current = window.setTimeout(pollOrder, ORDER_POLLING_INTERVAL)
      }

      pollingTimerRef.current = window.setTimeout(pollOrder, ORDER_POLLING_INTERVAL)
    },
    [navigate, stopOrderPolling],
  )

  const resumePendingPaymentPolling = useCallback(() => {
    const pendingPayment = getPendingPayment()

    if (!pendingPayment) {
      return
    }

    setIsPaying(true)
    startOrderPolling(pendingPayment.orderId)
  }, [startOrderPolling])

  useEffect(() => {
    const handlePageVisible = () => {
      if (!document.hidden) {
        resumePendingPaymentPolling()
      }
    }

    resumePendingPaymentPolling()
    window.addEventListener('pageshow', resumePendingPaymentPolling)
    window.addEventListener('focus', resumePendingPaymentPolling)
    document.addEventListener('visibilitychange', handlePageVisible)

    return () => {
      window.removeEventListener('pageshow', resumePendingPaymentPolling)
      window.removeEventListener('focus', resumePendingPaymentPolling)
      document.removeEventListener('visibilitychange', handlePageVisible)
    }
  }, [resumePendingPaymentPolling])

  const handlePay = useCallback(async () => {
    if (isPaying || !selectedPlan || !selectedPayType) {
      return
    }

    const openAlipayInCurrentTab = selectedPayType === 'alipay' && isIOSSafari()
    const alipayWindow = selectedPayType === 'alipay' && !openAlipayInCurrentTab ? window.open('', '_blank') : null
    const paymentAttemptId = paymentAttemptRef.current + 1
    paymentAttemptRef.current = paymentAttemptId
    setIsPaying(true)

    try {
      const { data } = await order({
        goods_id: selectedPlan.goods_id,
        pay_type: selectedPayType,
        source: '',
        pay_source: 'h5',
        extra: {},
      })

      if (paymentAttemptRef.current !== paymentAttemptId) {
        alipayWindow?.close()
        return
      }

      if (selectedPayType === 'alipay') {
        if (!data.orderId || !data.payParam) {
          alipayWindow?.close()
          window.alert('支付信息获取失败，请稍后重试')
          throw new Error('Missing Alipay orderId or payParam')
        }

        savePendingPayment(data.orderId)
        startOrderPolling(data.orderId)

        if (openAlipayInCurrentTab) {
          window.location.assign(data.payParam)
          return
        }

        if (alipayWindow) {
          alipayWindow.location.href = data.payParam
          return
        }

        if (!window.open(data.payParam, '_blank')) {
          clearPendingPayment(data.orderId)
          stopOrderPolling()
          window.alert('支付宝页面打开失败，请允许浏览器弹窗后重试')
          throw new Error('Failed to open Alipay window')
        }

        return
      }

      if (selectedPayType === 'weixin') {
        if (!data.orderId || !data.h5_url) {
          window.alert('微信支付信息获取失败，请稍后重试')
          throw new Error('Missing Weixin orderId or h5_url')
        }

        savePendingPayment(data.orderId)
        startOrderPolling(data.orderId)
        window.location.assign(getWeixinH5PayUrl(data.h5_url))
        return
      }

      navigate('/pay-result?success=true')
    } catch {
      alipayWindow?.close()
      stopOrderPolling()
      if (isMountedRef.current && paymentAttemptRef.current === paymentAttemptId) {
        setIsPaying(false)
      }
    }
  }, [isPaying, navigate, selectedPayType, selectedPlan, startOrderPolling, stopOrderPolling])

  const handleCurrentIndexChange = useCallback(
    (nextIndex: number | ((prevIndex: number) => number)) => {
      cancelPaymentFlow()
      setCurrentIndex(nextIndex)
    },
    [cancelPaymentFlow],
  )

  const handleSelectPayType = useCallback(
    (payType: string) => {
      if (payType !== selectedPayType) {
        cancelPaymentFlow()
      }

      setSelectedPayType(payType)
    },
    [cancelPaymentFlow, selectedPayType],
  )

  return (
    <div id="PayPage">
      <Header />
      <Plan
        canPay={Boolean(selectedPlan && selectedPayType)}
        currentIndex={currentIndex}
        goodsList={goodsList}
        isPaying={isPaying}
        onCurrentIndexChange={handleCurrentIndexChange}
        onPay={handlePay}
        selectedPlan={selectedPlan}
      />
      <PayMethod
        methods={supportedPayMethods}
        onSelectPayType={handleSelectPayType}
        selectedPayType={selectedPayType}
      />
      <ContactUs serviceUrl={serviceUrl} />
    </div>
  )
}

function Header() {
  return (
    <div className="header">
      <div className="tips">
        <span className="tip">安装包即将到期</span>
        <span className="date">2026-06-26 到期</span>
      </div>
      <div className="title">
        <div>
          安装包<span className="lock">解锁</span>
        </div>
        <div>解锁后即可使用</div>
      </div>
    </div>
  )
}

type PlanProps = {
  canPay: boolean
  currentIndex: number
  goodsList: GoodsItem[]
  isPaying: boolean
  onCurrentIndexChange: (nextIndex: number | ((prevIndex: number) => number)) => void
  onPay: () => void
  selectedPlan?: GoodsItem
}

function Plan({
  canPay,
  currentIndex,
  goodsList,
  isPaying,
  onCurrentIndexChange,
  onPay,
  selectedPlan,
}: PlanProps) {
  const [isAnimating, setIsAnimating] = useState(false)
  const touchStartXRef = useRef<number | null>(null)
  const touchStartYRef = useRef<number | null>(null)
  const touchDeltaXRef = useRef(0)
  const animationTimerRef = useRef<number | null>(null)
  const goodsCount = goodsList.length

  useEffect(() => {
    return () => {
      if (animationTimerRef.current !== null) {
        window.clearTimeout(animationTimerRef.current)
      }
    }
  }, [])

  const lockAnimation = () => {
    setIsAnimating(true)
    if (animationTimerRef.current !== null) {
      window.clearTimeout(animationTimerRef.current)
    }
    animationTimerRef.current = window.setTimeout(() => {
      setIsAnimating(false)
      animationTimerRef.current = null
    }, 320)
  }

  const changePlan = (direction: 'prev' | 'next') => {
    if (isAnimating || goodsCount === 0) {
      return
    }

    lockAnimation()
    onCurrentIndexChange((prevIndex) => {
      if (direction === 'prev') {
        return (prevIndex - 1 + goodsCount) % goodsCount
      }

      return (prevIndex + 1) % goodsCount
    })
  }

  const getPositionClassName = (index: number) => {
    if (goodsCount === 0) {
      return ''
    }

    const distance = (index - currentIndex + goodsCount) % goodsCount

    if (distance === 0) {
      return 'is-center selected'
    }

    if (distance === 1) {
      return 'is-right'
    }

    return 'is-left'
  }

  const handlePlanClick = (index: number) => {
    if (index === currentIndex || isAnimating || goodsCount === 0) {
      return
    }

    const distance = (index - currentIndex + goodsCount) % goodsCount
    changePlan(distance === 1 ? 'next' : 'prev')
  }

  const handleTouchStart = (event: React.TouchEvent<HTMLDivElement>) => {
    const touch = event.touches[0]
    touchStartXRef.current = touch.clientX
    touchStartYRef.current = touch.clientY
    touchDeltaXRef.current = 0
  }

  const handleTouchMove = (event: React.TouchEvent<HTMLDivElement>) => {
    if (touchStartXRef.current === null || touchStartYRef.current === null) {
      return
    }

    const touch = event.touches[0]
    const deltaX = touch.clientX - touchStartXRef.current
    const deltaY = touch.clientY - touchStartYRef.current

    if (Math.abs(deltaX) > Math.abs(deltaY)) {
      touchDeltaXRef.current = deltaX
      event.preventDefault()
    }
  }

  const handleTouchEnd = () => {
    const swipeThreshold = 36
    const deltaX = touchDeltaXRef.current

    touchStartXRef.current = null
    touchStartYRef.current = null
    touchDeltaXRef.current = 0

    if (Math.abs(deltaX) < swipeThreshold || isAnimating || goodsCount === 0) {
      return
    }

    changePlan(deltaX < 0 ? 'next' : 'prev')
  }

  return (
    <>
      <div
        className="plan"
        onTouchEnd={handleTouchEnd}
        onTouchMove={handleTouchMove}
        onTouchStart={handleTouchStart}
      >
        <div className="plan-track">
          {goodsList.map((plan, index) => (
            <button
              className={`plan-item ${getPositionClassName(index)}`}
              key={plan.goods_id}
              onClick={() => handlePlanClick(index)}
              type="button"
            >
              {plan.recommend && (
                <span className="plan-recommend-badge">
                  <span>推荐</span>
                </span>
              )}
              <div className="plan-name">{plan.goods_name}</div>
              <div className="plan-price">
                <span className="plan-price-symbol">￥</span>
                {plan.price}
              </div>
              <div className="plan-origin-price">
                <span>￥</span>
                {plan.origin_price}
              </div>
              {index === currentIndex && (
                <img className="plan-selected-icon" src="/images/pay/icon-check-footer.png" alt="" />
              )}
            </button>
          ))}
        </div>
      </div>
      <button className="pay-button" disabled={isPaying || !canPay} onClick={onPay} type="button">
        {isPaying ? '支付处理中...' : selectedPlan ? `立即支付 ￥${selectedPlan.price}` : '暂无可支付商品'}
      </button>
    </>
  )
}

type PayMethodProps = {
  methods: PayMethodInfo[]
  onSelectPayType: (payType: string) => void
  selectedPayType: string
}

function PayMethod({ methods, onSelectPayType, selectedPayType }: PayMethodProps) {
  return (
    <div className="pay-method">
      <div className="pay-method-title">支付方式</div>
      {methods.length > 0 && (
        <div className="pay-method-list">
          {methods.map((method) => (
            <div
              className={`pay-method-item ${selectedPayType === method.code ? 'selected' : ''}`}
              key={method.code}
              onClick={() => onSelectPayType(method.code)}
            >
              <div className="pay-method-info">
                <img className="pay-method-icon" src={method.icon} alt={method.name} />
                <div className="pay-method-name">{method.name}</div>
              </div>
              <img
                className="pay-method-selected-icon"
                src={selectedPayType === method.code ? '/images/pay/icon-checked.png' : '/images/pay/icon-uncheck.png'}
                alt="选中"
              />
            </div>
          ))}
        </div>
      )}
    </div>
  )
}

type ContactUsProps = {
  serviceUrl: string
}

function ContactUs({ serviceUrl }: ContactUsProps) {
  return (
    <a className="contact-us" href={serviceUrl || undefined} target="_blank" rel="noreferrer">
      <img src="/images/pay/icon-concat.png" alt="" />
      <span>客服</span>
    </a>
  )
}

export default PayPage
