import { ref, computed, onMounted, onUnmounted } 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 { DEFAULT_CHAIN_ID } from '@/config';
import { createRetryManager, formatGraphqlError, resolveGraphqlEndpoint } from '@/utils/graphqlRetry';

const GRAPHQL_TIMEOUT_MS = 15_000;

interface ProtocolStatsRecord {
  kraikenTotalSupply: string;
  mintedLastDay: string;
  mintedLastWeek: string;
  burnedLastDay: string;
  burnedLastWeek: string;
  taxPaidLastDay: string;
  taxPaidLastWeek: string;
  holderCount: number;
  lastRecenterTimestamp: string;
  lastRecenterTick: number;
  recentersLastDay: number;
  recentersLastWeek: number;
  lastEthReserve: string;
}

const rawProtocolStats = ref<ProtocolStatsRecord | null>(null);
const loading = ref(false);
const initialized = ref(false);
const statsError = ref<string | null>(null);
const activeChainId = ref<number>(DEFAULT_CHAIN_ID);

const retryManager = createRetryManager(loadProtocolStats, activeChainId);

export async function loadProtocolStatsData(chainId: number, endpointOverride?: string): Promise<ProtocolStatsRecord> {
  const endpoint = resolveGraphqlEndpoint(chainId, endpointOverride);
  logger.info(`loadProtocolStatsData for chainId: ${chainId}`);

  const res = await axios.post(
    endpoint,
    {
      query: `query ProtocolStats {
        statss(where: { id: "0x01" }) {
          items {
            kraikenTotalSupply
            mintedLastDay
            mintedLastWeek
            burnedLastDay
            burnedLastWeek
            taxPaidLastDay
            taxPaidLastWeek
            holderCount
            lastRecenterTimestamp
            lastRecenterTick
            recentersLastDay
            recentersLastWeek
            lastEthReserve
          }
        }
      }`,
    },
    { 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 items = res.data?.data?.statss?.items;
  if (!Array.isArray(items) || items.length === 0) {
    throw new Error('Protocol stats not found in GraphQL response');
  }

  return items[0] as ProtocolStatsRecord;
}

export async function loadProtocolStats(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) {
    rawProtocolStats.value = null;
    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 {
    rawProtocolStats.value = await loadProtocolStatsData(targetChainId, endpoint);
    statsError.value = null;
    retryManager.reset();
    retryManager.clear();
  } catch (error) {
    rawProtocolStats.value = null;
    statsError.value = formatGraphqlError(error);
    retryManager.schedule();
  } finally {
    loading.value = false;
    initialized.value = true;
  }
}

let unwatch: ReturnType<typeof watchChainId> | null = null;

export function useProtocolStats(chainId: number = DEFAULT_CHAIN_ID) {
  activeChainId.value = chainId;

  onMounted(async () => {
    if (rawProtocolStats.value === null && !loading.value) {
      await loadProtocolStats(activeChainId.value);
    }

    if (!unwatch) {
      unwatch = watchChainId(config as Config, {
        async onChange(nextChainId) {
          const resolvedChainId = nextChainId ?? DEFAULT_CHAIN_ID;
          activeChainId.value = resolvedChainId;
          await loadProtocolStats(resolvedChainId);
        },
      });
    }
  });

  onUnmounted(() => {
    retryManager.clear();
    if (unwatch) {
      unwatch();
      unwatch = null;
    }
  });

  // Helper to format bigint values with decimals
  const formatToken = (value: string, decimals: number = 18): number => {
    try {
      const bigintValue = BigInt(value);
      const divisor = BigInt(10 ** decimals);
      return Number(bigintValue) / Number(divisor);
    } catch {
      return 0;
    }
  };

  // Compute derived metrics
  const supplyGrowthPercent = computed(() => {
    if (!rawProtocolStats.value) return 0;
    const totalSupply = formatToken(rawProtocolStats.value.kraikenTotalSupply);
    if (totalSupply === 0) return 0;

    const minted = formatToken(rawProtocolStats.value.mintedLastDay);
    const burned = formatToken(rawProtocolStats.value.burnedLastDay);
    const netChange = minted - burned;

    return (netChange / totalSupply) * 100;
  });

  const netExpansionRate = computed(() => {
    if (!rawProtocolStats.value) return 0;
    const totalSupply = formatToken(rawProtocolStats.value.kraikenTotalSupply);
    if (totalSupply === 0) return 0;

    const minted = formatToken(rawProtocolStats.value.mintedLastDay);
    const burned = formatToken(rawProtocolStats.value.burnedLastDay);
    const tax = formatToken(rawProtocolStats.value.taxPaidLastDay);
    const netChange = minted - burned - tax;

    return (netChange / totalSupply) * 100;
  });

  const ethReserve = computed(() => {
    if (!rawProtocolStats.value) return 0;
    return formatToken(rawProtocolStats.value.lastEthReserve);
  });

  const taxPaidLastDay = computed(() => {
    if (!rawProtocolStats.value) return 0;
    return formatToken(rawProtocolStats.value.taxPaidLastDay);
  });

  const taxPaidLastWeek = computed(() => {
    if (!rawProtocolStats.value) return 0;
    return formatToken(rawProtocolStats.value.taxPaidLastWeek);
  });

  const holderCount = computed(() => {
    return rawProtocolStats.value?.holderCount ?? 0;
  });

  const recentersLastDay = computed(() => {
    return rawProtocolStats.value?.recentersLastDay ?? 0;
  });

  const recentersLastWeek = computed(() => {
    return rawProtocolStats.value?.recentersLastWeek ?? 0;
  });

  const lastRecenterTimestamp = computed(() => {
    if (!rawProtocolStats.value) return null;
    try {
      const timestamp = BigInt(rawProtocolStats.value.lastRecenterTimestamp);
      return Number(timestamp);
    } catch {
      return null;
    }
  });

  // Calculate annualized indicative rate based on 7-day data
  const annualizedRate = computed(() => {
    if (!rawProtocolStats.value) return 0;
    const totalSupply = formatToken(rawProtocolStats.value.kraikenTotalSupply);
    if (totalSupply === 0) return 0;

    const mintedWeek = formatToken(rawProtocolStats.value.mintedLastWeek);
    const burnedWeek = formatToken(rawProtocolStats.value.burnedLastWeek);
    const netWeek = mintedWeek - burnedWeek;

    const weeklyRate = netWeek / totalSupply;
    const annualRate = weeklyRate * 52; // 52 weeks in a year

    return annualRate * 100;
  });

  return {
    loading,
    initialized,
    statsError,
    supplyGrowthPercent,
    netExpansionRate,
    ethReserve,
    taxPaidLastDay,
    taxPaidLastWeek,
    holderCount,
    recentersLastDay,
    recentersLastWeek,
    lastRecenterTimestamp,
    annualizedRate,
  };
}
