import { ref, onMounted } from 'vue';
import { DEFAULT_CHAIN_ID } from '@/config';
import { resolveGraphqlEndpoint } from '@/utils/graphqlRetry';
import { KRAIKEN_LIB_VERSION, STACK_META_ID } from 'kraiken-lib/version';

const WEB_APP_VERSION = __APP_VERSION__;

type GraphqlError = { message?: string | null };
type GraphqlResponse<T> = {
  data?: T;
  errors?: GraphqlError[];
};

interface StackMetaRecord {
  contractVersion?: number | null;
  ponderVersion?: string | null;
  kraikenLibVersion?: number | null;
  updatedAt?: string | null;
}

interface StackMetaResult {
  stackMeta?: StackMetaRecord | null;
}

export interface StackVersions {
  contractVersion: number | null;
  ponderVersion: string | null;
  indexerLibVersion: number | null;
  kraikenLibVersion: number;
  webAppVersion: string;
  updatedAt: bigint | null;
}

export interface VersionStatus {
  isValid: boolean;
  error?: string;
  contractVersion?: number;
  indexerLibVersion?: number;
  ponderVersion?: string | null;
  libVersion: number;
  webAppVersion: string;
}

const STACK_META_QUERY = `
  query StackMeta($id: String!) {
    stackMeta(id: $id) {
      contractVersion
      ponderVersion
      kraikenLibVersion
      updatedAt
    }
  }
`;

const versionStatus = ref<VersionStatus>({
  isValid: true,
  libVersion: KRAIKEN_LIB_VERSION,
  webAppVersion: WEB_APP_VERSION,
});

const stackVersions = ref<StackVersions>({
  contractVersion: null,
  ponderVersion: null,
  indexerLibVersion: null,
  kraikenLibVersion: KRAIKEN_LIB_VERSION,
  webAppVersion: WEB_APP_VERSION,
  updatedAt: null,
});

const isChecking = ref(false);
const hasChecked = ref(false);

function formatError(error: unknown): string {
  if (error instanceof Error && error.message) {
    return error.message;
  }
  if (typeof error === 'string' && error.length > 0) {
    return error;
  }
  return 'Failed to load stack version metadata';
}

function sanitizeNumber(value: unknown): number | null {
  const parsed = typeof value === 'number' ? value : Number(value ?? Number.NaN);
  return Number.isFinite(parsed) ? parsed : null;
}

export function useVersionCheck() {
  async function checkVersions(graphqlEndpointOverride?: string) {
    if (isChecking.value) return versionStatus.value;

    isChecking.value = true;

    try {
      const endpoint = resolveGraphqlEndpoint(DEFAULT_CHAIN_ID, graphqlEndpointOverride);
      const response = await fetch(endpoint, {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({
          query: STACK_META_QUERY,
          variables: { id: STACK_META_ID },
        }),
      });

      if (!response.ok) {
        throw new Error(`GraphQL stackMeta request failed (HTTP ${response.status})`);
      }

      const payload = (await response.json()) as GraphqlResponse<StackMetaResult>;

      const apiErrors = payload.errors?.filter(err => err?.message);
      if (apiErrors && apiErrors.length > 0) {
        throw new Error(apiErrors.map(err => err.message).join(', '));
      }

      const meta = payload.data?.stackMeta;
      if (!meta) {
        throw new Error('Stack metadata unavailable (stackMeta query returned null)');
      }

      const contractVersion = sanitizeNumber(meta.contractVersion);
      const ponderVersion = meta.ponderVersion ?? null;
      const indexerLibVersion = sanitizeNumber(meta.kraikenLibVersion);
      const updatedAt = meta.updatedAt ? BigInt(meta.updatedAt) : null;

      const libMismatch = indexerLibVersion !== null && indexerLibVersion !== KRAIKEN_LIB_VERSION;

      stackVersions.value = {
        contractVersion,
        ponderVersion,
        indexerLibVersion,
        kraikenLibVersion: KRAIKEN_LIB_VERSION,
        webAppVersion: WEB_APP_VERSION,
        updatedAt,
      };

      versionStatus.value = {
        isValid: !libMismatch,
        error: libMismatch
          ? `Stack mismatch: Ponder reports kraiken-lib v${indexerLibVersion} (frontend expects v${KRAIKEN_LIB_VERSION}). Restart the stack to rebuild containers.`
          : undefined,
        contractVersion: contractVersion ?? undefined,
        indexerLibVersion: indexerLibVersion ?? undefined,
        ponderVersion,
        libVersion: KRAIKEN_LIB_VERSION,
        webAppVersion: WEB_APP_VERSION,
      };
    } catch (error) {
      versionStatus.value = {
        isValid: false,
        error: formatError(error),
        contractVersion: stackVersions.value.contractVersion ?? undefined,
        indexerLibVersion: stackVersions.value.indexerLibVersion ?? undefined,
        ponderVersion: stackVersions.value.ponderVersion,
        libVersion: KRAIKEN_LIB_VERSION,
        webAppVersion: WEB_APP_VERSION,
      };
    } finally {
      hasChecked.value = true;
      isChecking.value = false;
    }

    return versionStatus.value;
  }

  return {
    versionStatus,
    stackVersions,
    checkVersions,
    isChecking,
    hasChecked,
  };
}

export function useVersionCheckOnMount(graphqlUrl?: string) {
  const { versionStatus, stackVersions, checkVersions, isChecking, hasChecked } = useVersionCheck();

  onMounted(async () => {
    await checkVersions(graphqlUrl);
  });

  return {
    versionStatus,
    stackVersions,
    isChecking,
    hasChecked,
    checkVersions,
  };
}
