// core/types/tests/pipeline.spec.ts
// Tests for 5-phase context type hierarchy

import { describe, expect, test } from 'bun:test';
import type {
  IBaseContext,
  IChainConfig,
  IDecodedContext,
  IGasContext,
  IReadyContext,
  IStateDB,
  IWithSender,
} from '../interfaces';
import type { TStakeTx, TTransferTx } from '../lib/tx_pb';

describe('Pipeline Context Types', () => {
  const mockStateDB = {} as IStateDB;
  const mockConfig = { chainId: 'test', chainName: 'Test' } as IChainConfig;
  const mockIndexDB = {} as IBaseContext['indexdb'];

  describe('IBaseContext', () => {
    test('should require mandatory fields', () => {
      const context: IBaseContext = {
        txBase64: 'base64data',
        statedb: mockStateDB,
        config: mockConfig,
        indexdb: mockIndexDB,
      };
      expect(context.txBase64).toBe('base64data');
      expect(context.statedb).toBe(mockStateDB);
      expect(context.config).toBe(mockConfig);
    });

    test('should allow optional fields', () => {
      const context: IBaseContext = {
        txBase64: 'base64data',
        statedb: mockStateDB,
        config: mockConfig,
        indexdb: mockIndexDB,
        logger: { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },
        extra: { key: 'value' },
      };
      expect(context.logger).toBeDefined();
      expect(context.extra?.key).toBe('value');
    });
  });

  describe('IDecodedContext', () => {
    test('should include tx and itx fields', () => {
      const context: IDecodedContext<TTransferTx> = {
        txBase64: 'base64data',
        statedb: mockStateDB,
        config: mockConfig,
        indexdb: mockIndexDB,
        tx: {
          from: 'z123',
          chainId: 'test',
          nonce: 1,
          pk: new Uint8Array(),
          itx: { typeUrl: '', value: new Uint8Array() },
        },
        txHash: 'hash123',
        txTime: '2024-01-01T00:00:00Z',
        txSize: 256,
        txBaseGas: true,
        itx: { to: 'z456', value: '100', tokens: [] },
        txType: 'fg:t:transfer',
      };
      expect(context.txHash).toBe('hash123');
      expect(context.txType).toBe('fg:t:transfer');
      expect(context.itx?.to).toBe('z456');
    });
  });

  describe('IGasContext', () => {
    test('should include gas calculation fields', () => {
      const context: IGasContext = {
        txBase64: 'base64data',
        statedb: mockStateDB,
        config: mockConfig,
        indexdb: mockIndexDB,
        tx: {
          from: 'z123',
          chainId: 'test',
          nonce: 1,
          pk: new Uint8Array(),
          itx: { typeUrl: '', value: new Uint8Array() },
        },
        txHash: 'hash123',
        txTime: '2024-01-01T00:00:00Z',
        txSize: 256,
        txBaseGas: true,
        itx: {},
        txType: 'fg:t:transfer',
        gasEstimate: { create: 1, update: 2, payment: 0 },
        senderUpdates: {},
        gasPaid: false,
      };
      expect(context.gasEstimate?.create).toBe(1);
      expect(context.gasPaid).toBe(false);
    });
  });

  describe('IReadyContext', () => {
    test('should include state snapshot', () => {
      const context: IReadyContext = {
        txBase64: 'base64data',
        statedb: mockStateDB,
        config: mockConfig,
        indexdb: mockIndexDB,
        tx: {
          from: 'z123',
          chainId: 'test',
          nonce: 1,
          pk: new Uint8Array(),
          itx: { typeUrl: '', value: new Uint8Array() },
        },
        txHash: 'hash123',
        txTime: '2024-01-01T00:00:00Z',
        txSize: 256,
        txBaseGas: true,
        itx: {},
        txType: 'fg:t:transfer',
        stateSnapshot: { z123: { address: 'z123', tokens: {} } },
      };
      expect(context.stateSnapshot).toBeDefined();
      expect(context.stateSnapshot.z123).toBeDefined();
    });

    test('should support generic TItx', () => {
      type TransferContext = IReadyContext<TTransferTx>;
      const context: TransferContext = {
        txBase64: 'base64data',
        statedb: mockStateDB,
        config: mockConfig,
        indexdb: mockIndexDB,
        tx: {
          from: 'z123',
          chainId: 'test',
          nonce: 1,
          pk: new Uint8Array(),
          itx: { typeUrl: '', value: new Uint8Array() },
        },
        txHash: 'hash123',
        txTime: '2024-01-01T00:00:00Z',
        txSize: 256,
        txBaseGas: true,
        itx: { to: 'z456', value: '100', tokens: [] },
        txType: 'fg:t:transfer',
        stateSnapshot: {},
      };
      expect(context.itx?.to).toBe('z456');
      expect(context.itx?.value).toBe('100');
    });

    test('should support TStakeTx via generic', () => {
      type StakeContext = IReadyContext<TStakeTx>;
      const context: StakeContext = {
        txBase64: 'base64data',
        statedb: mockStateDB,
        config: mockConfig,
        indexdb: mockIndexDB,
        tx: {
          from: 'z123',
          chainId: 'test',
          nonce: 1,
          pk: new Uint8Array(),
          itx: { typeUrl: '', value: new Uint8Array() },
        },
        txHash: 'hash123',
        txTime: '2024-01-01T00:00:00Z',
        txSize: 256,
        txBaseGas: true,
        itx: { to: 'z456', tokens: [] },
        txType: 'fg:t:stake',
        stateSnapshot: {},
      };
      expect(context.itx?.to).toBe('z456');
    });
  });

  describe('Mixin Interfaces', () => {
    test('should allow combining phase types with mixins', () => {
      type TransferContext = IReadyContext<TTransferTx> & IWithSender;
      const context: TransferContext = {
        txBase64: 'base64data',
        statedb: mockStateDB,
        config: mockConfig,
        indexdb: mockIndexDB,
        tx: {
          from: 'z123',
          chainId: 'test',
          nonce: 1,
          pk: new Uint8Array(),
          itx: { typeUrl: '', value: new Uint8Array() },
        },
        txHash: 'hash123',
        txTime: '2024-01-01T00:00:00Z',
        txSize: 256,
        txBaseGas: true,
        itx: { to: 'z456', value: '100', tokens: [] },
        txType: 'fg:t:transfer',
        stateSnapshot: {},
        senderState: {
          address: 'z123',
          balance: '1000',
          nonce: '1',
          numTxs: '10',
          pk: 'pk',
          moniker: 'alice',
          tokens: {},
          context: { genesisTime: '', genesisTx: '' },
        },
      };
      expect(context.senderState.address).toBe('z123');
      expect(context.senderState.balance).toBe('1000');
    });
  });

  describe('Dynamic extension', () => {
    test('should allow protocol-specific fields via index signature', () => {
      const context: IReadyContext = {
        txBase64: 'base64data',
        statedb: mockStateDB,
        config: mockConfig,
        indexdb: mockIndexDB,
        tx: {
          from: 'z123',
          chainId: 'test',
          nonce: 1,
          pk: new Uint8Array(),
          itx: { typeUrl: '', value: new Uint8Array() },
        },
        txHash: 'hash123',
        txTime: '2024-01-01T00:00:00Z',
        txSize: 256,
        txBaseGas: true,
        itx: {},
        txType: 'fg:t:transfer',
        stateSnapshot: {},
        // Protocol-specific fields
        factoryState: { address: 'z789' },
        customField: 'value',
      };
      expect(context.factoryState).toBeDefined();
      expect(context.customField).toBe('value');
    });
  });
});
