/* eslint-disable @typescript-eslint/indent */
import type { TPaymentCurrency, TPaymentMethodExpanded } from '@blocklet/payment-types';
import { Alert } from '@mui/material';
import { useLocalStorageState, useRequest } from 'ahooks';
import type { Axios } from 'axios';
import { createContext, useContext, useEffect, useState } from 'react';

import axios from 'axios';
import { joinURL } from 'ufo';
import useBus from 'use-bus';
import api from '../libs/api';
import { getPrefix, PAYMENT_KIT_DID } from '../libs/util';
import { CachedRequest } from '../libs/cached-request';

export interface Settings {
  paymentMethods: TPaymentMethodExpanded[];
  baseCurrency: TPaymentCurrency;
}

export type PaymentContextType = {
  livemode: boolean;
  session: import('@arcblock/did-connect-react/lib/types').SessionContext['session'] & { user: any };
  connect: import('@arcblock/did-connect-react/lib/types').SessionContext['connectApi'];
  prefix: string;
  settings: Settings;
  refresh: (forceRefresh?: boolean) => void;
  getCurrency: (currencyId: string) => TPaymentCurrency | undefined;
  getMethod: (methodId: string) => TPaymentMethodExpanded | undefined;
  setLivemode: (livemode: boolean) => void;
  api: Axios;
  payable: boolean;
  setPayable: (status: boolean) => void;
  paymentState: {
    paying: boolean;
    stripePaying: boolean;
  };
  setPaymentState: (state: Partial<{ paying: boolean; stripePaying: boolean }>) => void;
};

export type PaymentContextProps = {
  session: import('@arcblock/did-connect-react/lib/types').SessionContext['session'];
  connect: import('@arcblock/did-connect-react/lib/types').SessionContext['connectApi'];
  children: any;
  // eslint-disable-next-line react/require-default-props
  baseUrl?: string;
  // eslint-disable-next-line react/require-default-props
  authToken?: string;
};

const formatData = (data: any) => {
  if (!data) {
    return {
      paymentMethods: [],
      baseCurrency: {},
    };
  }
  return {
    ...data,
    paymentMethods: data.paymentMethods || [],
    baseCurrency: data.baseCurrency || {},
  };
};

// @ts-ignore
const PaymentContext = createContext<PaymentContextType>({ api });
const { Provider, Consumer } = PaymentContext;

const getSettings = (forceRefresh = false) => {
  const livemode = localStorage.getItem('livemode') !== 'false';
  const cacheKey = `payment-settings-${window.location.pathname}-${livemode}`;

  const cachedRequest = new CachedRequest(cacheKey, () => api.get('/api/settings', { params: { livemode } }));

  return cachedRequest.fetch(forceRefresh);
};

const getCurrency = (currencyId: string, methods: TPaymentMethodExpanded[]) => {
  const currencies = methods.reduce((acc, x) => acc.concat(x.payment_currencies), [] as TPaymentCurrency[]);
  return currencies.find((x) => x.id === currencyId);
};

const getMethod = (methodId: string, methods: TPaymentMethodExpanded[]) => {
  return methods.find((x) => x.id === methodId);
};

function PaymentProvider({
  session,
  connect,
  children,
  baseUrl = undefined,
  authToken = undefined,
}: PaymentContextProps) {
  const [crossOriginLoading, setCrossOriginLoading] = useState(false);

  if (authToken) {
    window.__PAYMENT_KIT_AUTH_TOKEN = authToken;
  } else {
    window.__PAYMENT_KIT_AUTH_TOKEN = '';
  }

  // Fetch cross-origin blocklet info if needed
  useEffect(() => {
    const fetchCrossOriginBlockletInfo = async () => {
      if (!baseUrl) {
        window.__PAYMENT_KIT_BASE_URL = '';
        setCrossOriginLoading(false);
        return;
      }

      const tmp = new URL(baseUrl);
      // Same origin check
      if (tmp.origin === window.location.origin) {
        window.__PAYMENT_KIT_BASE_URL = '';
        setCrossOriginLoading(false);
        return;
      }

      setCrossOriginLoading(true);

      try {
        const scriptUrl = joinURL(tmp.origin, '__blocklet__.js?type=json');
        const cacheKey = `cross-origin-blocklet-${tmp.origin}`;

        const cachedRequest = new CachedRequest(
          cacheKey,
          () => axios.get(scriptUrl).then((res) => ({ data: res.data })),
          {
            strategy: 'session',
            ttl: 10 * 60 * 1000, // 10 minutes TTL
          }
        );

        const blockletInfo = await cachedRequest.fetch();
        const componentId = (blockletInfo?.componentId || '').split('/').pop();

        // Set base URL based on component type
        if (componentId === PAYMENT_KIT_DID) {
          window.__PAYMENT_KIT_BASE_URL = joinURL(tmp.origin, blockletInfo.prefix || '/');
        } else {
          const component = (blockletInfo?.componentMountPoints || []).find((x: any) => x?.did === PAYMENT_KIT_DID);
          window.__PAYMENT_KIT_BASE_URL = component ? joinURL(tmp.origin, component.mountPoint) : baseUrl; // Fallback to original baseUrl
        }
      } catch (err) {
        console.warn(`Failed to fetch blocklet json from ${baseUrl}:`, err);
        // Fallback to original baseUrl on error
        window.__PAYMENT_KIT_BASE_URL = baseUrl;
      } finally {
        setCrossOriginLoading(false);
      }
    };

    fetchCrossOriginBlockletInfo();
  }, [baseUrl]);

  const [livemode, setLivemode] = useLocalStorageState('livemode', { defaultValue: true });

  const {
    data = {
      paymentMethods: [],
      baseCurrency: {},
    },
    error,
    run,
    loading,
  } = useRequest(getSettings, {
    refreshDeps: [livemode],
  });

  // Listen for settings changes and force refresh
  useBus(
    // @ts-ignore
    ['paymentMethod.created', 'paymentMethod.updated', 'paymentCurrency.added', 'paymentCurrency.updated'],
    () => run(true),
    [run]
  );

  const prefix = getPrefix();
  const [payable, setPayable] = useState(true);
  const [paymentState, setPaymentState] = useState({
    paying: false,
    stripePaying: false,
  });

  const updatePaymentState = (state: Partial<{ paying: boolean; stripePaying: boolean }>) => {
    setPaymentState((prev) => ({ ...prev, ...state }));
  };

  if (error) {
    return <Alert severity="error">{error.message}</Alert>;
  }

  if (loading || crossOriginLoading) {
    return null;
  }

  return (
    <Provider
      value={{
        session: session as any,
        connect,
        prefix,
        livemode: !!livemode,
        settings: formatData(data) as Settings,
        getCurrency: (currencyId: string) => getCurrency(currencyId, (data as Settings)?.paymentMethods || []),
        getMethod: (methodId: string) => getMethod(methodId, (data as Settings)?.paymentMethods || []),
        refresh: run,
        setLivemode,
        api,
        payable,
        setPayable,
        paymentState,
        setPaymentState: updatePaymentState,
      }}>
      {children}
    </Provider>
  );
}

function usePaymentContext() {
  const context = useContext(PaymentContext);
  return context;
}

export { PaymentContext, PaymentProvider, Consumer as SettingsConsumer, usePaymentContext };
