import React, {
  createContext,
  ReactNode,
  useCallback,
  useContext,
  useEffect,
  useState,
} from "react";
import { useAccount, useReadContract, useWriteContract } from "wagmi";
import contractAbi from "../../../out/RaidGeld.sol/RaidGeld.json";
import { Hash, parseEther } from "viem";
import contracts from "../../contract_address";
import WaitingForTxModal from "../components/WaitingForTxModal";

const { contractAddress, daoTokenAddress } = contracts;
const abi = contractAbi.abi;

export type UnitType = 0 | 1 | 2 | 3;
export type BossLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6;

export interface Player {
  created_at: bigint;
  last_raided_at: bigint;
  total_minted: bigint;
  total_rewards: bigint;
  n_runs: number;
  prestige_level: number;
  is_registered: boolean;
  has_active_session: boolean;
}
export interface Army {
  anointed: { level: number };
  apprentice: { level: number };
  champion: { level: number };
  moloch_denier: { level: number };
  profit_per_second: bigint;
}
export interface Boss {
  level: BossLevel;
  variants: [
    BossLevel,
    BossLevel,
    BossLevel,
    BossLevel,
    BossLevel,
    BossLevel,
    BossLevel
  ];
}

export interface PlayerContextType {
  isRegistered: boolean;
  player: null | Player;
  army: null | Army;
  boss: null | Boss;
  balance: bigint;
  register: (arg: "ETH" | "RGCVII") => void;
  raid: () => void;
  battleWithBoss: () => void;
  addUnit: (unit: UnitType, amount?: number) => void;
}

const PlayerContext = createContext<PlayerContextType>({
  isRegistered: false,
  player: null,
  army: null,
  boss: null,
  balance: BigInt(0),
  register: () => {},
  raid: () => {},
  battleWithBoss: () => {},
  addUnit: () => {},
});

const PlayerProvider = ({ children }: { children: ReactNode }) => {
  const { address, isConnected } = useAccount();
  const { writeContract, error } = useWriteContract();
  const [[txHash, callbackFn], setHashAndCallback] = useState<
    [Hash | null, () => void]
  >([null, () => {}]);

  useEffect(() => {
    console.error(error?.message);
  }, [error]);

  const resetHashAndCallback = useCallback(() => {
    setHashAndCallback([null, () => {}]);
  }, []);

  const { data: isRegistered } = useReadContract({
    address: contractAddress,
    abi,
    functionName: "isRegistered",
    args: [address],
    query: {
      enabled: isConnected,
      refetchInterval: 15,
    },
  });

  const { data: balance } = useReadContract({
    address: contractAddress,
    abi,
    functionName: "balanceOf",
    args: [address],
    query: {
      refetchInterval: 15,
      enabled: isConnected,
    },
  });

  const { data: player } = useReadContract({
    address: contractAddress,
    abi,
    functionName: "getPlayer",
    args: [address],
    query: {
      enabled: isConnected,
      refetchInterval: 15,
    },
  });

  const { data: army } = useReadContract({
    address: contractAddress,
    abi,
    functionName: "getArmy",
    args: [address],
    query: {
      enabled: isConnected,
      refetchInterval: 15,
    },
  });

  const { data: boss } = useReadContract({
    address: contractAddress,
    abi,
    functionName: "getBoss",
    args: [address],
    query: {
      enabled: isConnected,
      refetchInterval: 15,
    },
  });

  console.log(balance, player, army, boss);

  const register = useCallback(
    (arg: "RGCVII" | "ETH") => {
      if (arg === "ETH") {
        writeContract(
          {
            abi,
            address: contractAddress,
            functionName: "register_eth",
            value: parseEther("0.0005"),
          },
          {
            onSuccess: (hash) => {
              setHashAndCallback([hash, resetHashAndCallback]);
            },
          }
        );
      } else if (arg === "RGCVII") {
        writeContract(
          {
            abi,
            address: daoTokenAddress,
            functionName: "approve",
            args: [contractAddress, parseEther("500")],
          },
          {
            onSuccess: (hash) => {
              setHashAndCallback([
                hash,
                () =>
                  writeContract(
                    {
                      abi,
                      address: contractAddress,
                      functionName: "register_dao",
                    },
                    {
                      onSuccess: (hash) => {
                        setHashAndCallback([hash, resetHashAndCallback]);
                      },
                    }
                  ),
              ]);
            },
          }
        );
      }
    },
    [writeContract, resetHashAndCallback]
  );

  const raid = useCallback(() => {
    writeContract(
      {
        abi,
        address: contractAddress,
        functionName: "raid",
      },
      {
        onSuccess: (hash) => {
          setHashAndCallback([hash, resetHashAndCallback]);
        },
      }
    );
  }, [writeContract, resetHashAndCallback]);

  const addUnit = useCallback(
    (unit: UnitType, amount: number = 1) => {
      writeContract({
        abi,
        address: contractAddress,
        functionName: "addUnit",
        args: [unit, amount],
      });
    },
    [writeContract]
  );

  const battleWithBoss = useCallback(() => {
    writeContract({
      abi,
      address: contractAddress,
      functionName: "battle_with_boss",
    });
  }, [writeContract]);

  return (
    <PlayerContext.Provider
      value={{
        isRegistered: isRegistered as boolean,
        player: player as Player,
        army: army as Army,
        boss: boss as Boss,
        balance: balance as bigint,
        register,
        raid,
        addUnit,
        battleWithBoss,
      }}
    >
      {children}
      {txHash && <WaitingForTxModal hash={txHash} callbackFn={callbackFn} />}
    </PlayerContext.Provider>
  );
};

export const usePlayer = () => {
  return useContext(PlayerContext);
};

export default PlayerProvider;
