import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { createTxnBot, type TxnBotDependencies } from './service.js';
import type { BotConfigService } from './services/BotConfigService.js';
import type { BlockchainService } from './services/BlockchainService.js';
import type { GraphQLService } from './services/GraphQLService.js';

const BASE_CONFIG = {
  PROVIDER_URL: 'http://localhost:8545',
  PRIVATE_KEY: '0xdeadbeef000000000000000000000000000000000000000000000000deadbeef',
  LM_CONTRACT_ADDRESS: '0x0000000000000000000000000000000000000001',
  STAKE_CONTRACT_ADDRESS: '0x0000000000000000000000000000000000000002',
  GRAPHQL_ENDPOINT: 'http://localhost:42069/graphql',
  ENVIRONMENT: 'test',
  PORT: '3000',
};

function makeTx(hash: string) {
  return { hash, wait: (): Promise<null> => Promise.resolve(null) };
}

function makeDeps(
  blockchain?: Partial<{
    estimateRecenterGas: () => Promise<void>;
    recenter: () => Promise<ReturnType<typeof makeTx>>;
  }>,
): TxnBotDependencies {
  return {
    configService: {
      getConfig: () => ({ ...BASE_CONFIG }),
      getPort: () => '3000',
    } as unknown as BotConfigService,

    blockchainService: {
      estimateRecenterGas: blockchain?.estimateRecenterGas ?? (() => Promise.resolve()),
      recenter: blockchain?.recenter ?? (() => Promise.resolve(makeTx('0xabc'))),
      checkFunds: () => Promise.resolve('1.0'),
      payTax: () => Promise.resolve(makeTx('0xpay')),
    } as unknown as BlockchainService,

    graphQLService: {
      fetchActivePositions: () => Promise.resolve([]),
    } as unknown as GraphQLService,
  };
}

describe('evaluateRecenterOpportunity', () => {
  it('returns canRecenter: true when gas estimation succeeds', async () => {
    const bot = createTxnBot(makeDeps());
    const result = await bot.evaluateRecenterOpportunity();

    assert.equal(result.canRecenter, true);
    assert.equal(result.reason, null);
    assert.equal(result.error, null);
    assert.ok(result.checkedAtMs > 0);
  });

  it('returns canRecenter: false with extracted revert reason', async () => {
    const bot = createTxnBot(
      makeDeps({
        estimateRecenterGas: () =>
          Promise.reject({
            shortMessage: 'execution reverted: recenter not needed',
            message: 'execution reverted: recenter not needed',
          }),
      }),
    );
    const result = await bot.evaluateRecenterOpportunity();

    assert.equal(result.canRecenter, false);
    assert.equal(result.reason, 'recenter not needed');
    assert.equal(result.error, 'execution reverted: recenter not needed');
  });

  it('returns canRecenter: false with generic message when no revert prefix', async () => {
    const bot = createTxnBot(
      makeDeps({
        estimateRecenterGas: () => Promise.reject({ message: 'connection refused' }),
      }),
    );
    const result = await bot.evaluateRecenterOpportunity();

    assert.equal(result.canRecenter, false);
    assert.equal(result.reason, 'connection refused');
    assert.ok(result.checkedAtMs > 0);
  });
});

describe('attemptRecenter', () => {
  it('calls recenter() and returns executed: true when eligible', async () => {
    let recenterCalled = false;
    const bot = createTxnBot(
      makeDeps({
        recenter: () => {
          recenterCalled = true;
          return Promise.resolve(makeTx('0xdeadbeef'));
        },
      }),
    );
    const result = await bot.attemptRecenter();

    assert.equal(result.executed, true);
    assert.equal(result.txHash, '0xdeadbeef');
    assert.ok(recenterCalled, 'recenter() should have been called');
  });

  it('skips recenter() and returns executed: false when not eligible', async () => {
    let recenterCalled = false;
    const bot = createTxnBot(
      makeDeps({
        estimateRecenterGas: () => Promise.reject({ message: 'recenter not needed' }),
        recenter: () => {
          recenterCalled = true;
          return Promise.resolve(makeTx('0xabc'));
        },
      }),
    );
    const result = await bot.attemptRecenter();

    assert.equal(result.executed, false);
    assert.equal(recenterCalled, false, 'recenter() must not be called when not eligible');
    assert.ok(result.message);
  });

  it('propagates error thrown by recenter() — caught by liquidityLoop', async () => {
    const bot = createTxnBot(
      makeDeps({
        recenter: () => Promise.reject(new Error('tx submission failed')),
      }),
    );

    await assert.rejects(bot.attemptRecenter(), /tx submission failed/);
  });
});
