import { createRequire } from 'node:module';
import { isCompatibleVersion, getVersionMismatchError, KRAIKEN_LIB_VERSION, STACK_META_ID } from 'kraiken-lib/version';
import { stackMeta } from 'ponder:schema';
import type { Context } from 'ponder:registry';

const require = createRequire(import.meta.url);
const { version: PONDER_APP_VERSION } = require('../../package.json') as { version: string };

/**
 * Validates that the deployed Kraiken contract version is compatible
 * with this indexer's kraiken-lib version.
 *
 * MUST be called at Ponder startup before processing any events.
 * Fails hard (process.exit) on mismatch to prevent indexing wrong data.
 */
export async function validateContractVersion(context: Context): Promise<void> {
  const logger = context.logger || console;

  try {
    const contractVersion = await context.client.readContract({
      address: context.contracts.Kraiken.address,
      abi: context.contracts.Kraiken.abi,
      functionName: 'VERSION',
    });

    const versionNumber = Number(contractVersion);

    if (!isCompatibleVersion(versionNumber)) {
      logger.error(getVersionMismatchError(versionNumber, 'ponder'));
      process.exit(1);
    }

    const timestamp = BigInt(Math.floor(Date.now() / 1000));
    const metaPayload = {
      contractVersion: versionNumber,
      ponderVersion: PONDER_APP_VERSION ?? 'dev',
      kraikenLibVersion: KRAIKEN_LIB_VERSION,
      updatedAt: timestamp,
    };

    const existingMeta = await context.db.find(stackMeta, { id: STACK_META_ID });
    if (existingMeta) {
      await context.db.update(stackMeta, { id: STACK_META_ID }).set(metaPayload);
    } else {
      await context.db.insert(stackMeta).values({
        id: STACK_META_ID,
        ...metaPayload,
      });
    }

    logger.info(`✓ Contract version validated: v${versionNumber} (kraiken-lib v${KRAIKEN_LIB_VERSION})`);
  } catch (error) {
    logger.error('Failed to read contract VERSION:', error);
    logger.error('Ensure Kraiken contract has VERSION constant and is deployed correctly');
    process.exit(1);
  }
}
