import { useCallback, useEffect, useRef, useState } from 'react';
import { useInterval } from 'react-use';
import {
  applyDiscountCode,
  getPayUrl,
  initActiveOrder,
  initOrder,
  queryOrder,
  syncPaymentOrder,
  type PayUrlRequest,
  type PaymentChannel,
} from '@/c-api/order';
import { ORDER_STATUS } from '../constans';

interface PriceItem {
  price_name: string;
  price: string;
  is_discount?: boolean;
}

const MAX_TIMEOUT = 1000 * 60 * 3;
const COUNTDOWN_INTERVAL = 1000;
const NATIVE_SYNC_INTERVAL = 1000 * 5;
const DIRECT_NATIVE_PAYMENT_CHANNELS = new Set<PaymentChannel>([
  'alipay',
  'wechatpay',
]);

function shouldSyncNativePaymentChannel(
  paymentChannel?: PaymentChannel,
): paymentChannel is 'alipay' | 'wechatpay' {
  return Boolean(
    paymentChannel && DIRECT_NATIVE_PAYMENT_CHANNELS.has(paymentChannel),
  );
}

export interface PaymentInfoState {
  channel: string;
  qrUrl: string;
  status?: number;
  paymentChannel?: PaymentChannel;
  paymentPayload?: Record<string, any>;
}

interface UsePaymentFlowOptions {
  type?: string;
  payload?: Record<string, any>;
  courseId: string;
  isLoggedIn: boolean;
  onOrderPaid?: () => void;
}

interface OrderSnapshot {
  order_id: string;
  price: string;
  value_to_pay: string;
  price_item?: PriceItem[];
  status: number;
}

export interface PaymentActionParams {
  channel: string;
  paymentChannel?: PaymentChannel;
  snapshot?: OrderSnapshot | null;
}

export interface PaymentCouponParams extends PaymentActionParams {
  code: string;
}

export interface PaymentSyncParams {
  paymentChannel?: PaymentChannel;
}

const defaultPaymentInfo: PaymentInfoState = {
  channel: '',
  qrUrl: '',
  status: undefined,
  paymentChannel: undefined,
  paymentPayload: {},
};

export const usePaymentFlow = ({
  type,
  payload,
  courseId,
  isLoggedIn,
  onOrderPaid,
}: UsePaymentFlowOptions) => {
  const mountedRef = useRef(true);
  const nativeSyncLastAtRef = useRef(0);
  useEffect(() => {
    return () => {
      mountedRef.current = false;
    };
  }, []);

  const [orderId, setOrderIdState] = useState('');
  const orderIdRef = useRef('');
  const updateOrderId = useCallback((value: string) => {
    orderIdRef.current = value;
    setOrderIdState(value);
  }, []);

  const [price, setPrice] = useState('0');
  const [originalPrice, setOriginalPrice] = useState('');
  const [priceItems, setPriceItems] = useState<PriceItem[]>([]);
  const [couponCode, setCouponCode] = useState('');
  const [paymentInfo, setPaymentInfo] =
    useState<PaymentInfoState>(defaultPaymentInfo);
  const [isLoading, setIsLoading] = useState(false);
  const [initLoading, setInitLoading] = useState(true);
  const [isTimeout, setIsTimeout] = useState(false);
  const [isCompleted, setIsCompleted] = useState(false);
  const [countDownMs, setCountDownMs] = useState(MAX_TIMEOUT);
  const [pollingActive, setPollingActive] = useState(false);

  useEffect(() => {
    if (!isLoggedIn) {
      setInitLoading(false);
    }
  }, [isLoggedIn]);

  const updateFromOrder = useCallback(
    (snapshot?: OrderSnapshot | null) => {
      if (!snapshot) return;
      setPrice(snapshot.value_to_pay);
      setOriginalPrice(snapshot.price);
      setPriceItems(
        snapshot.price_item?.filter(item => item?.is_discount) || [],
      );
      const valueToPayNumber = Number(snapshot.value_to_pay);
      const isFreeOrder =
        !Number.isNaN(valueToPayNumber) && valueToPayNumber <= 0;
      if (snapshot.status === ORDER_STATUS.BUY_STATUS_SUCCESS || isFreeOrder) {
        setIsCompleted(true);
        setPollingActive(false);
        onOrderPaid?.();
      }
    },
    [onOrderPaid],
  );

  const initOrderUniform = useCallback(async () => {
    if (type === 'active') {
      const { recordId = '', action = '' } = (payload || {}) as {
        recordId?: string;
        action?: string;
      };
      return initActiveOrder({
        courseId,
        recordId,
        action,
      });
    }
    return initOrder(courseId);
  }, [courseId, payload, type]);

  const initializeOrder = useCallback(async () => {
    if (!isLoggedIn) {
      return null;
    }
    setIsLoading(true);
    setIsTimeout(false);
    setCountDownMs(MAX_TIMEOUT);
    setPaymentInfo(defaultPaymentInfo);
    nativeSyncLastAtRef.current = 0;
    try {
      const snapshot = await initOrderUniform();
      if (!mountedRef.current || !snapshot) {
        return snapshot;
      }
      updateOrderId(snapshot.order_id);
      setCouponCode('');
      setIsCompleted(
        snapshot.status === ORDER_STATUS.BUY_STATUS_SUCCESS ? true : false,
      );
      updateFromOrder(snapshot);
      return snapshot;
    } finally {
      if (mountedRef.current) {
        setIsLoading(false);
        setInitLoading(false);
      }
    }
  }, [initOrderUniform, isLoggedIn, updateFromOrder, updateOrderId]);

  const refreshPayment = useCallback(
    async ({ channel, paymentChannel, snapshot }: PaymentActionParams) => {
      if (!orderIdRef.current) return null;
      setIsLoading(true);
      try {
        const current =
          snapshot ||
          ((await queryOrder({
            orderId: orderIdRef.current,
          })) as OrderSnapshot | null);
        if (!mountedRef.current || !current) {
          return current;
        }
        updateFromOrder(current);
        const currentSnapshot = current;
        const valueToPayNumber = Number(currentSnapshot.value_to_pay);
        const isFreeOrder =
          !Number.isNaN(valueToPayNumber) && valueToPayNumber <= 0;
        if (
          currentSnapshot.status === ORDER_STATUS.BUY_STATUS_SUCCESS ||
          isFreeOrder
        ) {
          return current;
        }
        const payload = await getPayUrl({
          channel,
          orderId: orderIdRef.current,
          paymentChannel,
        } as PayUrlRequest);
        if (!mountedRef.current || !payload) {
          return payload;
        }
        setPaymentInfo({
          channel: payload.channel,
          qrUrl: typeof payload.qr_url === 'string' ? payload.qr_url : '',
          status: payload.status,
          paymentChannel: payload.payment_channel,
          paymentPayload: payload.payment_payload || {},
        });
        nativeSyncLastAtRef.current = 0;
        setIsTimeout(false);
        setCountDownMs(MAX_TIMEOUT);
        if (payload.status === ORDER_STATUS.BUY_STATUS_SUCCESS) {
          setIsCompleted(true);
          setPollingActive(false);
          onOrderPaid?.();
        } else {
          setPollingActive(true);
        }
        return payload;
      } finally {
        if (mountedRef.current) {
          setIsLoading(false);
        }
      }
    },
    [onOrderPaid, updateFromOrder],
  );

  const applyCoupon = useCallback(
    async ({ code, channel, paymentChannel }: PaymentCouponParams) => {
      if (!orderIdRef.current) return null;
      const resp = await applyDiscountCode({
        orderId: orderIdRef.current,
        code,
      });
      if (!mountedRef.current || !resp) {
        return resp;
      }
      setCouponCode(code);
      updateFromOrder(resp as OrderSnapshot);
      if (
        resp.status === ORDER_STATUS.BUY_STATUS_INIT ||
        resp.status === ORDER_STATUS.BUY_STATUS_TO_BE_PAID
      ) {
        await refreshPayment({
          channel,
          paymentChannel,
          snapshot: resp as OrderSnapshot,
        });
      }
      return resp;
    },
    [refreshPayment, updateFromOrder],
  );

  useInterval(
    async () => {
      setCountDownMs(prev => {
        if (prev <= COUNTDOWN_INTERVAL) {
          setIsTimeout(true);
          setPollingActive(false);
          return 0;
        }
        return prev - COUNTDOWN_INTERVAL;
      });

      if (!orderIdRef.current) {
        return;
      }
      const currentOrderId = orderIdRef.current;
      const nativePaymentChannel = paymentInfo.paymentChannel;
      if (shouldSyncNativePaymentChannel(nativePaymentChannel)) {
        const now = Date.now();
        if (now - nativeSyncLastAtRef.current >= NATIVE_SYNC_INTERVAL) {
          nativeSyncLastAtRef.current = now;
          try {
            await syncPaymentOrder({
              orderId: currentOrderId,
              paymentChannel: nativePaymentChannel,
            });
          } catch {
            // The regular order query below keeps polling while the provider sync is transiently unavailable.
          }
        }
      }

      const resp = await queryOrder({ orderId: currentOrderId });
      if (!mountedRef.current || !resp) {
        return;
      }
      updateFromOrder(resp as OrderSnapshot);
    },
    isLoggedIn && pollingActive ? COUNTDOWN_INTERVAL : null,
  );

  const syncOrderStatus = useCallback(
    async (params: PaymentSyncParams = {}) => {
      if (!orderIdRef.current) {
        return null;
      }
      const syncPaymentChannel =
        params.paymentChannel ||
        (shouldSyncNativePaymentChannel(paymentInfo.paymentChannel)
          ? paymentInfo.paymentChannel
          : undefined);
      if (syncPaymentChannel) {
        await syncPaymentOrder({
          orderId: orderIdRef.current,
          paymentChannel: syncPaymentChannel,
        });
      }
      const resp = await queryOrder({ orderId: orderIdRef.current });
      if (!mountedRef.current || !resp) {
        return resp;
      }
      updateFromOrder(resp as OrderSnapshot);
      return resp;
    },
    [paymentInfo.paymentChannel, updateFromOrder],
  );

  const resetState = useCallback(() => {
    updateOrderId('');
    setPrice('0');
    setOriginalPrice('');
    setPriceItems([]);
    setCouponCode('');
    setPaymentInfo(defaultPaymentInfo);
    nativeSyncLastAtRef.current = 0;
    setIsTimeout(false);
    setIsCompleted(false);
    setCountDownMs(MAX_TIMEOUT);
    setPollingActive(false);
  }, [updateOrderId]);

  return {
    orderId,
    price,
    originalPrice,
    priceItems,
    couponCode,
    setCouponCode,
    paymentInfo,
    isLoading,
    initLoading,
    isTimeout,
    isCompleted,
    countDownMs,
    initializeOrder,
    refreshPayment,
    applyCoupon,
    syncOrderStatus,
    resetState,
  };
};
