import { describe, it, expect } from 'vitest';
import { deriveBankId } from './index.js';
import type { PluginHookAgentContext, PluginConfig } from './types.js';

describe('deriveBankId', () => {
  const ctx: PluginHookAgentContext = {
    agentId: 'agent-123',
    channelId: 'channel-456',
    senderId: 'user-789',
    messageProvider: 'slack',
  };

  const baseConfig: PluginConfig = {
    dynamicBankId: true,
  };

  it('should use default isolation fields when not specified', () => {
    const bankId = deriveBankId(ctx, baseConfig);
    expect(bankId).toBe('agent-123::channel-456::user-789');
  });

  it('should default to dynamic bank ID when dynamicBankId is not specified', () => {
    const config: PluginConfig = {};
    const bankId = deriveBankId(ctx, config);
    expect(bankId).toBe('agent-123::channel-456::user-789');
  });

  it('should support ["agent", "user"] isolation', () => {
    const config: PluginConfig = { ...baseConfig, dynamicBankGranularity: ['agent', 'user'] };
    const bankId = deriveBankId(ctx, config);
    expect(bankId).toBe('agent-123::user-789');
  });

  it('should support ["user"] isolation', () => {
    const config: PluginConfig = { ...baseConfig, dynamicBankGranularity: ['user'] };
    const bankId = deriveBankId(ctx, config);
    expect(bankId).toBe('user-789');
  });

  it('should support ["agent"] isolation', () => {
    const config: PluginConfig = { ...baseConfig, dynamicBankGranularity: ['agent'] };
    const bankId = deriveBankId(ctx, config);
    expect(bankId).toBe('agent-123');
  });

  it('should support ["channel"] isolation', () => {
    const config: PluginConfig = { ...baseConfig, dynamicBankGranularity: ['channel'] };
    const bankId = deriveBankId(ctx, config);
    expect(bankId).toBe('channel-456');
  });

  it('should support ["provider"] isolation', () => {
    const config: PluginConfig = { ...baseConfig, dynamicBankGranularity: ['provider'] };
    const bankId = deriveBankId(ctx, config);
    expect(bankId).toBe('slack');
  });

  it('should support mixed fields including provider', () => {
    const config: PluginConfig = { ...baseConfig, dynamicBankGranularity: ['provider', 'user'] };
    const bankId = deriveBankId(ctx, config);
    expect(bankId).toBe('slack::user-789');
  });

  it('should prepend bankIdPrefix if set', () => {
    const config: PluginConfig = { ...baseConfig, bankIdPrefix: 'prod' };
    const bankId = deriveBankId(ctx, config);
    expect(bankId).toBe('prod-agent-123::channel-456::user-789');
  });

  it('should use fallback values for missing context fields', () => {
    const partialCtx: PluginHookAgentContext = {
      agentId: 'agent-123',
    };
    const bankId = deriveBankId(partialCtx, baseConfig);
    expect(bankId).toBe('agent-123::unknown::anonymous');
  });

  it('should parse sessionKey as fallback for missing channel and provider', () => {
    const ctxWithSession: PluginHookAgentContext = {
      agentId: 'my-agent',
      sessionKey: 'agent:my-agent:telegram:group:-100123456:topic:7',
    };
    const config: PluginConfig = { ...baseConfig, dynamicBankGranularity: ['agent', 'channel', 'provider'] };
    const bankId = deriveBankId(ctxWithSession, config);
    expect(bankId).toBe('my-agent::group%3A-100123456%3Atopic%3A7::telegram');
  });

  it('should return "openclaw" if dynamicBankId is false', () => {
    const config: PluginConfig = { dynamicBankId: false };
    const bankId = deriveBankId(ctx, config);
    expect(bankId).toBe('openclaw');
  });

  it('should encode segments to prevent separator collisions', () => {
    const ctxWithSeparator: PluginHookAgentContext = {
      agentId: 'a::b',
      channelId: 'c',
      senderId: 'user-1',
    };
    const ctxWithoutSeparator: PluginHookAgentContext = {
      agentId: 'a',
      channelId: 'b::c',
      senderId: 'user-1',
    };
    const bankId1 = deriveBankId(ctxWithSeparator, baseConfig);
    const bankId2 = deriveBankId(ctxWithoutSeparator, baseConfig);
    // These must NOT collide
    expect(bankId1).not.toBe(bankId2);
    // Segment delimiters are encoded, preserving unique values.
    expect(bankId1).toBe('a%3A%3Ab::c::user-1');
    expect(bankId2).toBe('a::b%3A%3Ac::user-1');
  });
});
