import { ref, computed, onMounted, onUnmounted } from 'vue';
import axios from 'axios';
import { getAccount, watchAccount } from '@wagmi/core';
import type { WatchAccountReturnType } from '@wagmi/core';
import { config } from '@/wagmi';
import type { Config } from '@wagmi/core';
import { DEFAULT_CHAIN_ID } from '@/config';
import { resolveGraphqlEndpoint } from '@/utils/graphqlRetry';
import logger from '@/utils/logger';

export interface SnatchEvent {
  id: string;
  timestamp: string;
  tokenAmount: string;
  ethAmount: string;
  txHash: string;
}

const GRAPHQL_TIMEOUT_MS = 15_000;
const STORAGE_KEY_PREFIX = 'snatch-seen-';

const unseenSnatches = ref<SnatchEvent[]>([]);
const allRecentSnatches = ref<SnatchEvent[]>([]);
const isOpen = ref(false);

let unwatchAccount: WatchAccountReturnType | null = null;
let consumerCount = 0;

function storageKey(address: string): string {
  return `${STORAGE_KEY_PREFIX}${address.toLowerCase()}`;
}

function getLastSeen(address: string): string {
  return localStorage.getItem(storageKey(address)) ?? '0';
}

function setLastSeen(address: string, timestamp: string): void {
  localStorage.setItem(storageKey(address), timestamp);
}

async function fetchSnatchEvents(endpoint: string, address: string, since: string | null): Promise<SnatchEvent[]> {
  const holder = address.toLowerCase();
  const where: { holder: string; type: string; timestamp_gt?: string } = { holder, type: 'snatch_out' };
  if (since !== null) {
    where.timestamp_gt = since;
  }
  const res = await axios.post(
    endpoint,
    {
      query: `query SnatchEvents($where: transactionsFilter!) {
        transactionss(
          where: $where
          orderBy: "timestamp"
          orderDirection: "desc"
          limit: 20
        ) {
          items { id timestamp tokenAmount ethAmount txHash }
        }
      }`,
      variables: { where },
    },
    { timeout: GRAPHQL_TIMEOUT_MS }
  );

  const errors = res.data?.errors;
  if (Array.isArray(errors) && errors.length > 0) {
    logger.info('useSnatchNotifications GraphQL errors', errors);
    return [];
  }
  return (res.data?.data?.transactionss?.items ?? []) as SnatchEvent[];
}

async function refresh(chainId: number): Promise<void> {
  const account = getAccount(config as Config);
  if (!account.address) {
    unseenSnatches.value = [];
    allRecentSnatches.value = [];
    return;
  }

  let endpoint: string;
  try {
    endpoint = resolveGraphqlEndpoint(chainId);
  } catch {
    unseenSnatches.value = [];
    allRecentSnatches.value = [];
    return;
  }

  try {
    const since = getLastSeen(account.address);
    const [unseen, all] = await Promise.all([
      fetchSnatchEvents(endpoint, account.address, since),
      fetchSnatchEvents(endpoint, account.address, null),
    ]);
    unseenSnatches.value = unseen;
    allRecentSnatches.value = all;
  } catch (err) {
    logger.info('useSnatchNotifications fetch failed', err);
  }
}

function markSeen(): void {
  const account = getAccount(config as Config);
  if (!account.address) return;

  // Advance last-seen to the most recent event timestamp
  const latestTs = allRecentSnatches.value[0]?.timestamp ?? unseenSnatches.value[0]?.timestamp;
  if (latestTs) {
    setLastSeen(account.address, latestTs);
  }
  // Clear badge; allRecentSnatches stays populated so the panel still shows history
  unseenSnatches.value = [];
}

export function useSnatchNotifications(chainId: number = DEFAULT_CHAIN_ID) {
  const unseenCount = computed(() => unseenSnatches.value.length);

  function open(): void {
    isOpen.value = true;
    markSeen();
  }

  function close(): void {
    isOpen.value = false;
  }

  function toggle(): void {
    if (isOpen.value) {
      close();
    } else {
      open();
    }
  }

  onMounted(async () => {
    consumerCount += 1;

    if (consumerCount === 1) {
      unwatchAccount = watchAccount(config as Config, {
        async onChange() {
          await refresh(chainId);
        },
      });
    }

    await refresh(chainId);
  });

  onUnmounted(() => {
    consumerCount = Math.max(0, consumerCount - 1);
    if (consumerCount === 0 && unwatchAccount) {
      unwatchAccount();
      unwatchAccount = null;
    }
  });

  return {
    unseenCount,
    unseenSnatches,
    allRecentSnatches,
    isOpen,
    open,
    close,
    toggle,
    refresh: () => refresh(chainId),
  };
}
