import type { MoonwellClient } from "../../client/createMoonwellClient.js";
import { getEnvironmentFromArgs } from "../../common/index.js";
import type { OptionalNetworkParameterType } from "../../common/types.js";
import type { Chain, Environment } from "../../environments/index.js";
import type { CirculatingSupplySnapshot } from "../../types/circulatingSupply.js";
import { getWithRetry, postWithRetry } from "../axiosWithRetry.js";

export type GetCirculatingSupplySnapshotsParameters<
  environments,
  network extends Chain | undefined,
> = OptionalNetworkParameterType<environments, network>;

export type GetCirculatingSupplySnapshotsReturnType = Promise<
  CirculatingSupplySnapshot[]
>;

interface LunarCirculatingSupplySnapshot {
  id: string;
  chainId: number;
  tokenAddress: string;
  tokenLabel: string;
  timestamp: number;
  blockNumber: string;
  totalSupply: string;
  excludedBalance: string;
  circulatingSupply: string;
}

interface LunarPaginatedResponse<T> {
  results: T[];
  nextCursor: string | null;
}

async function fetchCirculatingSupplyFromLunar(
  lunarIndexerUrl: string,
  chainId: number,
): Promise<LunarCirculatingSupplySnapshot[]> {
  const all: LunarCirculatingSupplySnapshot[] = [];
  let cursor: string | null = null;
  const MAX_PAGES = 10;
  let page = 0;

  do {
    const params: Record<string, string> = { limit: "1000" };
    if (cursor) {
      params.cursor = cursor;
    }

    const response = await getWithRetry<
      LunarPaginatedResponse<LunarCirculatingSupplySnapshot>
    >(`${lunarIndexerUrl}/api/v1/staking/circulating-supply/${chainId}`, {
      params,
    });

    all.push(...response.data.results);
    cursor = response.data.nextCursor;
    page++;
  } while (cursor !== null && page < MAX_PAGES);

  return all;
}

export async function getCirculatingSupplySnapshots<
  environments,
  Network extends Chain | undefined,
>(
  client: MoonwellClient,
  args?: GetCirculatingSupplySnapshotsParameters<environments, Network>,
): GetCirculatingSupplySnapshotsReturnType {
  const environment = getEnvironmentFromArgs(client, args);

  if (!environment) {
    return [];
  }

  if (!environment.lunarIndexerUrl) {
    return environment.indexerUrl
      ? fetchCirculatingSupplyFromPonder(environment)
      : [];
  }

  try {
    const items = await fetchCirculatingSupplyFromLunar(
      environment.lunarIndexerUrl,
      environment.chainId,
    );
    return items.flatMap((item) => {
      const token = Object.values(environment.config.tokens).find(
        (t) => t.address.toLowerCase() === item.tokenAddress.toLowerCase(),
      );
      if (!token) return [];
      return [
        {
          chainId: item.chainId,
          token,
          circulatingSupply: Number.parseFloat(item.circulatingSupply),
          totalSupply: item.totalSupply,
          excludedBalance: item.excludedBalance,
          timestamp: item.timestamp,
        },
      ];
    });
  } catch (error) {
    console.warn(
      `[getCirculatingSupplySnapshots] Lunar Indexer failed for chain ${environment.chainId}:`,
      error,
    );
    environment.onError?.(error, {
      source: "circulating-supply",
      chainId: environment.chainId,
    });
    return [];
  }
}

async function fetchCirculatingSupplyFromPonder(
  environment: Environment,
): Promise<CirculatingSupplySnapshot[]> {
  if (!environment.indexerUrl) return [];
  try {
    const response = await postWithRetry<{
      data: {
        circulatingSupplyDailySnapshots: {
          items: {
            chainId: number;
            tokenAddress: string;
            circulatingSupply: number;
            timestamp: number;
          }[];
        };
      };
    }>(environment.indexerUrl, {
      query: `
          {
            circulatingSupplyDailySnapshots(
              where: { chainId: ${environment.chainId} }
              orderBy: "timestamp"
              orderDirection: "desc"
              limit: 1000
            ) {
              items {
                chainId
                tokenAddress
                circulatingSupply
                timestamp
              }
            }
          }
        `,
    });

    if (
      response.status === 200 &&
      response.data?.data?.circulatingSupplyDailySnapshots
    ) {
      return response.data.data.circulatingSupplyDailySnapshots.items.flatMap(
        (item) => {
          const token = Object.values(environment.config.tokens).find(
            (t) => t.address.toLowerCase() === item.tokenAddress.toLowerCase(),
          );
          if (!token) return [];
          return [
            {
              chainId: item.chainId,
              token,
              circulatingSupply: item.circulatingSupply,
              timestamp: item.timestamp,
            },
          ];
        },
      );
    }
    return [];
  } catch (ex) {
    console.error(
      "An error occurred while fetching getCirculatingSupplySnapshots...",
      ex,
    );
    return [];
  }
}
