/**
 * Unit tests for FallbackStrategy judgment strategies
 *
 * Tests AutoSelectStrategy and canApply logic for all strategies.
 * Strategies requiring external agent calls (ReportBased, ResponseBased,
 * AgentConsult) are tested for canApply and input validation only.
 */

import { describe, it, expect } from 'vitest';
import {
  AutoSelectStrategy,
  ReportBasedStrategy,
  ResponseBasedStrategy,
  AgentConsultStrategy,
  JudgmentStrategyFactory,
  type JudgmentContext,
} from '../core/piece/judgment/FallbackStrategy.js';
import { makeMovement } from './test-helpers.js';

function makeContext(overrides: Partial<JudgmentContext> = {}): JudgmentContext {
  return {
    step: makeMovement(),
    cwd: '/tmp/test',
    ...overrides,
  };
}

describe('AutoSelectStrategy', () => {
  const strategy = new AutoSelectStrategy();

  it('should have name "AutoSelect"', () => {
    expect(strategy.name).toBe('AutoSelect');
  });

  describe('canApply', () => {
    it('should return true when movement has exactly one rule', () => {
      const ctx = makeContext({
        step: makeMovement({
          rules: [{ condition: 'done', next: 'COMPLETE' }],
        }),
      });
      expect(strategy.canApply(ctx)).toBe(true);
    });

    it('should return false when movement has multiple rules', () => {
      const ctx = makeContext({
        step: makeMovement({
          rules: [
            { condition: 'approved', next: 'implement' },
            { condition: 'rejected', next: 'review' },
          ],
        }),
      });
      expect(strategy.canApply(ctx)).toBe(false);
    });

    it('should return false when movement has no rules', () => {
      const ctx = makeContext({
        step: makeMovement({ rules: undefined }),
      });
      expect(strategy.canApply(ctx)).toBe(false);
    });
  });

  describe('execute', () => {
    it('should return auto-selected tag for single-branch movement', async () => {
      const ctx = makeContext({
        step: makeMovement({
          name: 'review',
          rules: [{ condition: 'done', next: 'COMPLETE' }],
        }),
      });

      const result = await strategy.execute(ctx);
      expect(result.success).toBe(true);
      expect(result.tag).toBe('[REVIEW:1]');
    });
  });
});

describe('ReportBasedStrategy', () => {
  const strategy = new ReportBasedStrategy();

  it('should have name "ReportBased"', () => {
    expect(strategy.name).toBe('ReportBased');
  });

  describe('canApply', () => {
    it('should return true when reportDir and outputContracts are present', () => {
      const ctx = makeContext({
        reportDir: '/tmp/reports',
        step: makeMovement({
          outputContracts: [{ name: 'report.md' }],
        }),
      });
      expect(strategy.canApply(ctx)).toBe(true);
    });

    it('should return false when reportDir is missing', () => {
      const ctx = makeContext({
        step: makeMovement({
          outputContracts: [{ name: 'report.md' }],
        }),
      });
      expect(strategy.canApply(ctx)).toBe(false);
    });

    it('should return false when outputContracts is empty', () => {
      const ctx = makeContext({
        reportDir: '/tmp/reports',
        step: makeMovement({ outputContracts: [] }),
      });
      expect(strategy.canApply(ctx)).toBe(false);
    });

    it('should return false when outputContracts is undefined', () => {
      const ctx = makeContext({
        reportDir: '/tmp/reports',
        step: makeMovement(),
      });
      expect(strategy.canApply(ctx)).toBe(false);
    });
  });
});

describe('ResponseBasedStrategy', () => {
  const strategy = new ResponseBasedStrategy();

  it('should have name "ResponseBased"', () => {
    expect(strategy.name).toBe('ResponseBased');
  });

  describe('canApply', () => {
    it('should return true when lastResponse is non-empty', () => {
      const ctx = makeContext({ lastResponse: 'some response' });
      expect(strategy.canApply(ctx)).toBe(true);
    });

    it('should return false when lastResponse is undefined', () => {
      const ctx = makeContext({ lastResponse: undefined });
      expect(strategy.canApply(ctx)).toBe(false);
    });

    it('should return false when lastResponse is empty string', () => {
      const ctx = makeContext({ lastResponse: '' });
      expect(strategy.canApply(ctx)).toBe(false);
    });
  });
});

describe('AgentConsultStrategy', () => {
  const strategy = new AgentConsultStrategy();

  it('should have name "AgentConsult"', () => {
    expect(strategy.name).toBe('AgentConsult');
  });

  describe('canApply', () => {
    it('should return true when sessionId is non-empty', () => {
      const ctx = makeContext({ sessionId: 'session-123' });
      expect(strategy.canApply(ctx)).toBe(true);
    });

    it('should return false when sessionId is undefined', () => {
      const ctx = makeContext({ sessionId: undefined });
      expect(strategy.canApply(ctx)).toBe(false);
    });

    it('should return false when sessionId is empty string', () => {
      const ctx = makeContext({ sessionId: '' });
      expect(strategy.canApply(ctx)).toBe(false);
    });
  });

  describe('execute', () => {
    it('should return failure when sessionId is not provided', async () => {
      const ctx = makeContext({ sessionId: undefined });
      const result = await strategy.execute(ctx);
      expect(result.success).toBe(false);
      expect(result.reason).toBe('Session ID not provided');
    });
  });
});

describe('JudgmentStrategyFactory', () => {
  it('should create strategies in correct priority order', () => {
    const strategies = JudgmentStrategyFactory.createStrategies();
    expect(strategies).toHaveLength(4);
    expect(strategies[0]!.name).toBe('AutoSelect');
    expect(strategies[1]!.name).toBe('ReportBased');
    expect(strategies[2]!.name).toBe('ResponseBased');
    expect(strategies[3]!.name).toBe('AgentConsult');
  });
});
