import { ref, reactive, computed } from 'vue';
import axios from 'axios';
import { watchChainId } from '@wagmi/core';
import type { Config } from '@wagmi/core';
import { config } from '@/wagmi';
import logger from '@/utils/logger';
import type { WatchBlocksReturnType } from 'viem';
import { weiToNumber } from 'kraiken-lib/format';
import { minStake as stakeMinStake } from '@/contracts/stake';
import { DEFAULT_CHAIN_ID } from '@/config';
import { createRetryManager, formatGraphqlError, resolveGraphqlEndpoint } from '@/utils/graphqlRetry';
const demo = sessionStorage.getItem('demo') === 'true';

const GRAPHQL_TIMEOUT_MS = 15_000;

interface StatsRecord {
  burnNextHourProjected: string;
  burnedLastDay: string;
  burnedLastWeek: string;
  id: string;
  minStake: string;
  mintNextHourProjected: string;
  mintedLastDay: string;
  mintedLastWeek: string;
  outstandingStake: string;
  kraikenTotalSupply: string;
  stakeTotalSupply: string;
  ringBufferPointer: number;
  totalBurned: string;
  totalMinted: string;
}

const rawStatsCollections = ref<Array<StatsRecord>>([]);
const loading = ref(false);
const initialized = ref(false);
const statsError = ref<string | null>(null);
const activeChainId = ref<number>(DEFAULT_CHAIN_ID);

const retryManager = createRetryManager(loadStats, activeChainId);

export async function loadStatsCollection(chainId: number, endpointOverride?: string) {
  const endpoint = resolveGraphqlEndpoint(chainId, endpointOverride);
  logger.info(`loadStatsCollection for chainId: ${chainId}`);
  const res = await axios.post(
    endpoint,
    {
      query: `query StatsQuery {
			stats(id: "0x01") {
				minStake
				burnNextHourProjected
				burnedLastDay
				burnedLastWeek
				id
				mintNextHourProjected
				mintedLastDay
				mintedLastWeek
				outstandingStake
				kraikenTotalSupply
				stakeTotalSupply
				ringBufferPointer
				totalBurned
				totalMinted
			}
		}`,
    },
    { timeout: GRAPHQL_TIMEOUT_MS }
  );

  const errors = res.data?.errors;
  if (Array.isArray(errors) && errors.length > 0) {
    throw new Error(errors.map((err: unknown) => (err as { message?: string })?.message ?? 'GraphQL error').join(', '));
  }

  const stats = res.data?.data?.stats as StatsRecord | undefined;
  if (!stats) {
    throw new Error('Stats entity not found in GraphQL response');
  }

  return [{ ...stats, kraikenTotalSupply: stats.kraikenTotalSupply }];
}

const profit7d = computed(() => {
  if (!statsCollection.value) {
    return 0n;
  }
  const mintedLastWeek = BigInt(statsCollection.value.mintedLastWeek);
  const burnedLastWeek = BigInt(statsCollection.value.burnedLastWeek);
  const totalMinted = BigInt(statsCollection.value.totalMinted);
  const totalBurned = BigInt(statsCollection.value.totalBurned);
  const denominator = totalMinted - totalBurned;
  if (denominator === 0n) {
    return 0n;
  }
  return (mintedLastWeek - burnedLastWeek) / denominator;
});
const nettoToken7d = computed(() => {
  if (!statsCollection.value) {
    return 0n;
  }

  return BigInt(statsCollection.value.mintedLastWeek) - BigInt(statsCollection.value.burnedLastWeek);
});

const statsCollection = computed(() => {
  if (rawStatsCollections.value?.length > 0) {
    return rawStatsCollections.value[0];
  } else {
    return undefined;
  }
});

const outstandingStake = computed(() => {
  if (demo) {
    // outStandingStake = 1990000000000000000000000n;
    return 2000000000000000000000000n;
  }
  if (rawStatsCollections.value?.length > 0) {
    return BigInt(rawStatsCollections.value[0].outstandingStake);
  } else {
    return 0n;
  }
});

const kraikenTotalSupply = computed(() => {
  if (rawStatsCollections.value?.length > 0) {
    return BigInt(rawStatsCollections.value[0].kraikenTotalSupply);
  } else {
    return 0n;
  }
});

const stakeTotalSupply = computed(() => {
  if (rawStatsCollections.value?.length > 0) {
    return BigInt(rawStatsCollections.value[0].stakeTotalSupply);
  } else {
    return 0n;
  }
});

const minStake = computed(() => {
  if (rawStatsCollections.value?.length > 0) {
    return BigInt(rawStatsCollections.value[0].minStake);
  } else {
    return 0n;
  }
});

//Total Supply Change / 7d=mintedLastWeek−burnedLastWeek
const totalSupplyChange7d = computed(() => {
  if (rawStatsCollections.value?.length > 0) {
    return BigInt(rawStatsCollections.value[0].mintedLastWeek) - BigInt(rawStatsCollections.value[0].burnedLastWeek);
  } else {
    return 0n;
  }
});

//totalsupply Change7d / harbtotalsupply
const inflation7d = computed(() => {
  if (rawStatsCollections.value?.length > 0 && BigInt(rawStatsCollections.value[0].kraikenTotalSupply) > 0n) {
    return BigInt(rawStatsCollections.value[0].mintedLastWeek) - BigInt(rawStatsCollections.value[0].burnedLastWeek);
  } else {
    return 0n;
  }
});

const stakeableSupply = computed(() => {
  if (rawStatsCollections.value?.length > 0 && BigInt(rawStatsCollections.value[0].kraikenTotalSupply) > 0n) {
    return stakeTotalSupply.value / 5n;
  } else {
    return 0n;
  }
});

//maxSlots
const maxSlots = computed(() => {
  if (rawStatsCollections.value?.length > 0 && BigInt(rawStatsCollections.value[0].kraikenTotalSupply) > 0n) {
    return (weiToNumber(stakeTotalSupply.value, 18) * 0.2) / 100;
  } else {
    return 0;
  }
});

const claimedSlots = computed(() => {
  if (stakeTotalSupply.value > 0n) {
    const stakeableSupplyNumber = weiToNumber(stakeableSupply.value, 18);
    const outstandingStakeNumber = weiToNumber(outstandingStake.value, 18);
    return (outstandingStakeNumber / stakeableSupplyNumber) * maxSlots.value;
  } else {
    return 0;
  }
});

export async function loadStats(chainId?: number) {
  loading.value = true;

  const targetChainId = typeof chainId === 'number' ? chainId : (activeChainId.value ?? DEFAULT_CHAIN_ID);
  activeChainId.value = targetChainId;

  let endpoint: string;
  try {
    endpoint = resolveGraphqlEndpoint(targetChainId);
  } catch (error) {
    rawStatsCollections.value = [];
    statsError.value = error instanceof Error ? error.message : 'GraphQL endpoint not configured for this chain.';
    retryManager.clear();
    retryManager.reset();
    loading.value = false;
    initialized.value = true;
    return;
  }

  try {
    rawStatsCollections.value = await loadStatsCollection(targetChainId, endpoint);
    statsError.value = null;
    retryManager.reset();
    retryManager.clear();
    stakeMinStake.value = minStake.value;
  } catch (error) {
    rawStatsCollections.value = [];
    statsError.value = formatGraphqlError(error);
    stakeMinStake.value = 0n;
    retryManager.schedule();
  } finally {
    loading.value = false;
    initialized.value = true;
  }
}

import { onMounted, onUnmounted } from 'vue';

let unwatch: WatchBlocksReturnType | null = null;
export function useStatCollection(chainId: number = DEFAULT_CHAIN_ID) {
  activeChainId.value = chainId;

  onMounted(async () => {
    //initial loading stats
    if (rawStatsCollections.value?.length === 0 && !loading.value) {
      await loadStats(activeChainId.value);
    }
  });
  if (!unwatch) {
    //chain Switch reload stats for other chain
    unwatch = watchChainId(config as Config, {
      async onChange(nextChainId) {
        const resolvedChainId = nextChainId ?? DEFAULT_CHAIN_ID;
        activeChainId.value = resolvedChainId;
        await loadStats(resolvedChainId);
      },
    });
  }
  onUnmounted(() => {
    retryManager.clear();
    if (unwatch) {
      unwatch();
    }
  });

  return reactive({
    profit7d,
    nettoToken7d,
    inflation7d,
    outstandingStake,
    kraikenTotalSupply,
    stakeTotalSupply,
    totalSupplyChange7d,
    initialized,
    maxSlots,
    stakeableSupply,
    claimedSlots,
    statsError,
    loading,
    minStake,
  });
}
