import type { TSetting } from '@blocklet/payment-types';
import { useRequest } from 'ahooks';
import type { Axios } from 'axios';
import { createContext, useContext, useState } from 'react';

import Toast from '@arcblock/ux/lib/Toast';
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
import { Button, Stack, Typography } from '@mui/material';
import api from '../libs/api';
import { formatError, getPaymentKitComponent, openDonationSettings } from '../libs/util';
import { CachedRequest } from '../libs/cached-request';
import ConfirmDialog from '../components/confirm';
import { usePaymentContext } from './payment';

export interface DonateConfigSettings {
  amount?: {
    presets?: string[];
    preset?: string;
    custom: boolean;
    minimum?: string;
    maximum?: string;
  };
  btnText?: string;
  historyType?: 'table' | 'avatar';
}

export type DonateContextType = {
  settings: TSetting;
  refresh: (forceRefresh?: boolean) => void;
  updateSettings: (newSettings: DonateConfigSettings) => Promise<void>;
  api: Axios;
};

export type DonateContextProps = {
  mountLocation: string;
  description: string;
  defaultSettings?: DonateConfigSettings;
  children: any;
  active?: boolean;
  enableDonate?: boolean;
};

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

const fetchDonateSetting = (
  params: {
    mountLocation: string;
    description: string;
    defaultSettings?: DonateConfigSettings;
    active?: boolean;
    componentDid?: string;
  },
  forceRefresh = false
) => {
  const livemode = localStorage.getItem('livemode') !== 'false';
  const cacheKey = `donate-settings-${params.mountLocation}-${livemode}`;

  const cachedRequest = new CachedRequest(
    cacheKey,
    () =>
      api.post('/api/settings', {
        ...params,
        type: 'donate',
        livemode,
        settings: params.defaultSettings,
      }),
    {
      ttl: 1000 * 60 * 60,
      strategy: 'local',
    }
  );
  return cachedRequest.fetch(forceRefresh);
};

function DonateProvider({
  mountLocation,
  description,
  defaultSettings = {},
  children,
  active = true,
  enableDonate = false,
}: DonateContextProps) {
  const { t } = useLocaleContext();
  const [showConfirm, setShowConfirm] = useState(false);
  const { session } = usePaymentContext();
  const isAdmin = ['owner', 'admin'].includes(session?.user?.role);
  const {
    data = {
      settings: {},
      active: true,
    },
    error,
    run,
    loading,
  } = useRequest(
    (forceRender) =>
      fetchDonateSetting(
        {
          mountLocation,
          description,
          defaultSettings,
          active,
          componentDid: window.blocklet?.componentId?.split('/').pop(),
        },
        forceRender
      ),
    {
      refreshDeps: [mountLocation],
      onError: (err) => {
        Toast.error(formatError(err));
      },
    }
  );

  const updateSettings = async (newSettings: DonateConfigSettings) => {
    try {
      const livemode = localStorage.getItem('livemode') !== 'false';
      await api.put(`/api/settings/${mountLocation}`, {
        livemode,
        settings: newSettings,
      });
      run(true);
      Toast.success(t('common.saved'));
    } catch (err) {
      Toast.error(formatError(err));
      throw err;
    }
  };

  const supportPaymentKit = getPaymentKitComponent();

  const handleEnable = async () => {
    if (!enableDonate || !data || (data as TSetting)?.active) return;
    try {
      await api.put(`/api/settings/${(data as TSetting).id}`, { active: true });
      if (supportPaymentKit) {
        setShowConfirm(true);
      } else {
        Toast.success(t('payment.checkout.donation.enableSuccess'));
        run(true);
      }
    } catch (err) {
      Toast.error(formatError(err));
    }
  };

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

  return (
    <Provider
      value={{
        settings: data as TSetting,
        refresh: run,
        updateSettings,
        api,
      }}>
      {(data as TSetting)?.active === false ? (
        <>
          {enableDonate && isAdmin && (
            <Stack spacing={1} sx={{ alignItems: 'center' }}>
              <Typography
                sx={{
                  color: 'text.secondary',
                }}>
                {t('payment.checkout.donation.inactive')}
              </Typography>
              <Button
                size="small"
                variant="outlined"
                color="primary"
                onClick={handleEnable}
                sx={{ width: 'fit-content' }}>
                {t('payment.checkout.donation.enable')}
              </Button>
            </Stack>
          )}

          {showConfirm && (
            <ConfirmDialog
              title={t('payment.checkout.donation.enableSuccess')}
              message={t('payment.checkout.donation.configPrompt')}
              cancel={t('payment.checkout.donation.later')}
              confirm={t('payment.checkout.donation.configNow')}
              onCancel={() => {
                setShowConfirm(false);
                run(true);
              }}
              color="primary"
              onConfirm={() => {
                run(true);
                openDonationSettings(false);
                setShowConfirm(false);
              }}
            />
          )}
        </>
      ) : (
        children
      )}
    </Provider>
  );
}

function useDonateContext() {
  const context = useContext(DonateContext);
  return context;
}

export const clearDonateCache = (mountLocation: string) => {
  const livemode = localStorage.getItem('livemode') !== 'false';
  const cacheKey = `donate-settings-${mountLocation}-${livemode}`;
  localStorage.removeItem(cacheKey);
};

export const clearDonateSettings = async (mountLocation: string) => {
  try {
    const livemode = localStorage.getItem('livemode') !== 'false';
    await api.delete(`/api/settings/${mountLocation}`, {
      params: {
        livemode,
      },
    });
    clearDonateCache(mountLocation);
  } catch (err) {
    Toast.error(formatError(err));
  }
};

export { DonateContext, DonateProvider, Consumer as DonateConsumer, useDonateContext };
