import React, {
  createContext,
  useContext,
  useReducer,
  useEffect,
  useMemo,
  useRef,
  useCallback,
} from 'react';
import { useAppStatus } from './appStatus';
import {
  bulkDeleteGiftsLocal,
  bulkSaveGiftsLocal,
  deleteGiftLocal,
  getAllLocalGifts,
  saveGiftLocal,
  updateGiftLocal,
} from '../app/functions/gift/giftsStorage';
import {
  addGiftToDatabase,
  bulkAddGiftsToDatabase,
  bulkDeleteGiftsFromDatabase,
  deleteGift,
  handleGiftCheck,
  reloadGiftsOnDomesday,
  updateGiftInDatabase,
} from '../db';
import { useGlobalContextProvider } from './context';
import { getLocalStorageItem, setLocalStorageItem } from '../app/functions';
import { deriveKeyFromMnemonic } from '../app/functions/seed';
import { useKeysContext } from './keys';
import { randomBytes } from 'react-native-quick-crypto';
import { getPublicKey } from 'nostr-tools';
import { encriptMessage } from '../app/functions/messaging/encodingAndDecodingMessages';
import { createGiftUrl } from '../app/functions/gift/encodeDecodeSecret';
import { deriveSparkGiftMnemonic } from '../app/functions/gift/deriveGiftWallet';
import { GIFT_DERIVE_PATH_CUTOFF } from '../app/constants';

const initialState = {
  gifts: {},
  currentDerivedGiftIndex: 0,
  userUuid: null,
};

function giftReducer(state, action) {
  switch (action.type) {
    case 'LOAD_LOCAL_GIFTS':
      return {
        ...state,
        gifts: action.payload.reduce((map, g) => {
          map[g.uuid] = g;
          return map;
        }, {}),
      };

    case 'ADD_OR_UPDATE_GIFT':
      return {
        ...state,
        gifts: { ...state.gifts, [action.payload.uuid]: action.payload },
      };
    case 'BULK_ADD_GIFTS':
      return {
        ...state,
        gifts: {
          ...state.gifts,
          ...action.payload.reduce((map, g) => {
            map[g.uuid] = g;
            return map;
          }, {}),
        },
      };
    case 'BULK_DELETE_GIFTS': {
      const remaining = { ...state.gifts };
      action.payload.forEach(uuid => delete remaining[uuid]);
      return { ...state, gifts: remaining };
    }
    default:
      return state;
  }
}

const GiftContext = createContext(null);

export function GiftProvider({ children }) {
  const { accountMnemoinc } = useKeysContext();
  const { masterInfoObject } = useGlobalContextProvider();
  const { didGetToHomepage } = useAppStatus();
  const [state, dispatch] = useReducer(giftReducer, initialState);
  const isCheckingRefunds = useRef(null);

  const updateGiftList = useCallback(async () => {
    const updatedList = await getAllLocalGifts();
    dispatch({ type: 'LOAD_LOCAL_GIFTS', payload: updatedList });
    return updatedList;
  }, []);

  const saveGiftToCloud = useCallback(async giftObj => {
    try {
      const localObject = JSON.parse(JSON.stringify(giftObj));
      const localResponse = await saveGiftLocal(localObject);
      delete giftObj.claimURL;
      const serverResponse = await addGiftToDatabase(giftObj);

      if (!serverResponse || !localResponse)
        throw new Error('Unable to save gift');
      dispatch({ type: 'ADD_OR_UPDATE_GIFT', payload: localObject });
      return true;
    } catch (err) {
      console.log('error saving gift to cloud');
      return false;
    }
  }, []);

  const bulkSaveGiftsToCloud = useCallback(async gifts => {
    try {
      const localObjects = gifts.map(g => JSON.parse(JSON.stringify(g)));
      const [localResponse, serverResponse] = await Promise.all([
        bulkSaveGiftsLocal(localObjects),
        bulkAddGiftsToDatabase(gifts),
      ]);
      if (!localResponse || !serverResponse)
        throw new Error('Unable to bulk save gifts');
      dispatch({ type: 'BULK_ADD_GIFTS', payload: localObjects });
      return true;
    } catch (err) {
      console.log('error bulk saving gifts to cloud', err);
      return false;
    }
  }, []);

  const bulkDeleteGiftsFromCloudAndLocal = useCallback(async uuids => {
    try {
      const [localResponse, serverResponse] = await Promise.all([
        bulkDeleteGiftsLocal(uuids),
        bulkDeleteGiftsFromDatabase(uuids),
      ]);
      if (!localResponse || !serverResponse)
        throw new Error('Unable to bulk delete gifts');
      dispatch({
        type: 'BULK_DELETE_GIFTS',
        payload: uuids,
      });
      return true;
    } catch (err) {
      console.log('error bulk deleting gifts from cloud and local', err);
      return false;
    }
  }, []);

  const deleteGiftFromCloudAndLocal = useCallback(
    async UUID => {
      try {
        await deleteGiftLocal(UUID);
        const response = await deleteGift(UUID);

        if (!response) throw new Error('Unable to delete gift from remote ');
        await updateGiftList();
        return true;
      } catch (err) {
        console.log('error saving gift to cloud');
        return false;
      }
    },
    [updateGiftList],
  );

  const checkForRefunds = useCallback(async giftList => {
    try {
      if (isCheckingRefunds.current) return;
      isCheckingRefunds.current = true;
      const localGifts = await (giftList
        ? Promise.resolve(giftList)
        : getAllLocalGifts());

      const giftArray = Object.values(localGifts);
      const now = Date.now();

      const expiredGifts = giftArray.filter(item => {
        return item.state === 'Unclaimed' && now >= item.expireTime;
      });
      console.log(expiredGifts, 'expired gifts');

      if (expiredGifts.length === 0) {
        console.log('No expired gifts to check');
        return;
      }

      console.log(`Checking ${expiredGifts.length} expired gifts...`);

      const checkPromises = expiredGifts.map(card =>
        handleGiftCheck(card.uuid)
          .then(response => ({ card, response }))
          .catch(error => {
            console.error(`Error checking gift ${card.uuid}:`, error);
            return { card, response: null };
          }),
      );

      const results = await Promise.all(checkPromises);

      // Batch database updates
      const updatePromises = results
        .filter(({ response }) => response?.didWork)
        .map(async ({ card, response }) => {
          console.log(card, response);
          try {
            if (response.wasClaimed) {
              await deleteGift(card.uuid);
            }

            await updateGiftLocal(card.uuid, {
              state: response.wasClaimed ? 'Claimed' : 'Expired',
            });

            console.log(
              `Updated gift ${card.uuid}:`,
              response.wasClaimed ? 'Claimed' : 'Expired',
            );
          } catch (error) {
            console.error(`Error updating gift ${card.uuid}:`, error);
          }
        });

      await Promise.all(updatePromises);
      await updateGiftList();
      console.log(`Processed ${updatePromises.length} gift updates`);
    } catch (err) {
      console.log('error checking for gift refunds', err.message);
    } finally {
      isCheckingRefunds.current = false;
    }
  }, [updateGiftList]);

  const handleGiftRestoreOnDomeseday = useCallback(async giftList => {
    // If we have gifts that means we are not restoring and do not need to get gifts in database
    if (giftList?.length) return;

    const didCheckDBForGifts = JSON.parse(
      await getLocalStorageItem('checkForOutstandingGifts'),
    );

    // We already checked for outstanding gifts and none exist so don't check again
    if (didCheckDBForGifts) return;

    const outstandingGifts = await reloadGiftsOnDomesday(masterInfoObject.uuid);

    // If no gifts exist, mark as checked and return
    if (!outstandingGifts.length) {
      await setLocalStorageItem(
        'checkForOutstandingGifts',
        JSON.stringify(true),
      );
      return;
    }

    const now = Date.now();

    // Process all gifts in parallel and wait for completion
    const reconstructedGifts = await Promise.all(
      outstandingGifts.map(async item => {
        let giftWalletMnemonic;

        if (item.createdTime > GIFT_DERIVE_PATH_CUTOFF) {
          giftWalletMnemonic = await deriveSparkGiftMnemonic(
            accountMnemoinc,
            item.giftNum,
          );
        } else {
          giftWalletMnemonic = await deriveKeyFromMnemonic(
            accountMnemoinc,
            item.giftNum,
          );
        }

        // Gift is expired - just add restore key
        if (item.expireTime < now) {
          const expiredGift = {
            ...item,
            restoreKey: giftWalletMnemonic.derivedMnemonic,
          };
          await saveGiftLocal(expiredGift);
          return expiredGift;
        } else {
          // Active gift - update with new secret for sharing
          const randomSecret = randomBytes(32);
          const randomPubkey = getPublicKey(randomSecret);
          const encryptedMnemonic = encriptMessage(
            randomSecret,
            randomPubkey,
            giftWalletMnemonic.derivedMnemonic,
          );
          const urls = createGiftUrl(item.uuid, randomSecret);

          const updatedGift = {
            ...item,
            claimURL: urls.webUrl,
            encryptedText: encryptedMnemonic,
          };

          await Promise.all([
            updateGiftInDatabase(updatedGift),
            saveGiftLocal(updatedGift),
          ]);

          return updatedGift;
        }
      }),
    );

    dispatch({ type: 'BULK_ADD_GIFTS', payload: reconstructedGifts });
    await setLocalStorageItem('checkForOutstandingGifts', JSON.stringify(true));
  }, [accountMnemoinc, masterInfoObject.uuid]);

  useEffect(() => {
    if (!didGetToHomepage) return;
    (async () => {
      const giftList = await updateGiftList();
      await checkForRefunds(giftList);
      await handleGiftRestoreOnDomeseday(giftList);
    })();
  }, [didGetToHomepage]);

  const { giftsArray, expiredGiftsArray } = useMemo(() => {
    const now = Date.now();
    const all = [];
    const expired = [];

    Object.values(state.gifts).forEach(gift => {
      all.push(gift);
      if (
        now >= gift.expireTime &&
        gift.state?.toLowerCase() !== 'claimed' &&
        gift.state?.toLowerCase() !== 'reclaimed'
      ) {
        expired.push(gift);
      }
    });

    return {
      giftsArray: all.sort((a, b) => b.giftNum - a.giftNum),
      expiredGiftsArray: expired.sort((a, b) => b.giftNum - a.giftNum),
    };
  }, [state.gifts]);

  const contextValue = useMemo(
    () => ({
      ...state,
      saveGiftToCloud,
      bulkSaveGiftsToCloud,
      checkForRefunds,
      deleteGiftFromCloudAndLocal,
      bulkDeleteGiftsFromCloudAndLocal,
      updateGiftList,
      giftsArray,
      expiredGiftsArray,
    }),
    [
      state,
      saveGiftToCloud,
      bulkSaveGiftsToCloud,
      checkForRefunds,
      deleteGiftFromCloudAndLocal,
      bulkDeleteGiftsFromCloudAndLocal,
      updateGiftList,
      giftsArray,
      expiredGiftsArray,
    ],
  );

  return (
    <GiftContext.Provider value={contextValue}>{children}</GiftContext.Provider>
  );
}

export const useGifts = () => useContext(GiftContext);
