/**
 * Adapter Policy Contract Tests
 *
 * Verifies that skipRetry and skipNavWait flags correctly modify
 * action execution behavior:
 * - skipRetry: removes action.policy.retry before execution
 * - skipNavWait: sets ctx.execution.skipNavWait for handlers
 */

import { describe, expect, it, vi, beforeEach } from 'vitest';
import { createStepExecutor } from '@/entrypoints/background/record-replay/actions/adapter';
import { createMockExecCtx, createMockStep } from './_test-helpers';

describe('adapter policy flags contract', () => {
  let registryExecute: ReturnType<typeof vi.fn>;
  let mockRegistry: any;

  beforeEach(() => {
    registryExecute = vi.fn(async () => ({ status: 'success' }));
    mockRegistry = {
      get: vi.fn((type: string) => ({ type })), // Returns truthy = handler exists
      execute: registryExecute,
    };
  });

  describe('step to action routing', () => {
    it.each([
      [
        'triggerEvent',
        {
          target: { candidates: [{ type: 'css', value: '#input' }] },
          event: 'change',
        },
      ],
      [
        'setAttribute',
        {
          target: { candidates: [{ type: 'css', value: '#input' }] },
          name: 'aria-expanded',
          value: 'true',
        },
      ],
    ])('routes %s steps through ActionRegistry', async (type, stepConfig) => {
      const executor = createStepExecutor(mockRegistry);

      await executor(createMockExecCtx(), createMockStep(type, stepConfig), 1, {});

      expect(registryExecute).toHaveBeenCalledTimes(1);
      const [, action] = registryExecute.mock.calls[0];
      expect(action.type).toBe(type);
      expect(action.params.target.candidates[0].selector).toBe('#input');
    });

    it('preserves extended locator metadata when converting legacy targets', async () => {
      const executor = createStepExecutor(mockRegistry);

      await executor(
        createMockExecCtx(),
        createMockStep('click', {
          target: {
            selector: '#submit',
            candidates: [
              {
                type: 'css',
                value: '#submit',
                source: 'recorded',
                strategy: 'testid',
                stability: {
                  score: 0.98,
                  signals: { usesId: true },
                },
              },
            ],
            fingerprint: 'button|id=submit|text=Save',
            domPath: [1, 2, 0],
            shadowHostChain: ['app-shell', '#inner-host'],
            frameContext: {
              kind: 'iframe',
              url: 'https://example.com/frame',
              frameSelector: 'iframe#checkout',
            },
          },
        }),
        1,
        {},
      );

      const [, action] = registryExecute.mock.calls[0];
      expect(action.params.target).toMatchObject({
        selector: '#submit',
        fingerprint: 'button|id=submit|text=Save',
        domPath: [1, 2, 0],
        shadowHostChain: ['app-shell', '#inner-host'],
        frameContext: {
          kind: 'iframe',
          url: 'https://example.com/frame',
          frameSelector: 'iframe#checkout',
        },
      });
      expect(action.params.target.candidates[0]).toMatchObject({
        type: 'css',
        selector: '#submit',
        source: 'recorded',
        strategy: 'testid',
        stability: {
          score: 0.98,
          signals: { usesId: true },
        },
      });
    });
  });

  describe('skipRetry flag', () => {
    it('removes action.policy.retry when skipRetry is true', async () => {
      const executor = createStepExecutor(mockRegistry);

      await executor(
        createMockExecCtx(),
        createMockStep('fill', {
          retry: { count: 3, intervalMs: 100, backoff: 'exp' },
          target: { candidates: [{ type: 'css', value: '#input' }] },
          value: 'test',
        }),
        1, // tabId
        { skipRetry: true },
      );

      expect(registryExecute).toHaveBeenCalledTimes(1);
      const [, action] = registryExecute.mock.calls[0];
      expect(action.policy?.retry).toBeUndefined();
    });

    it('preserves action.policy.retry when skipRetry is false', async () => {
      const executor = createStepExecutor(mockRegistry);

      await executor(
        createMockExecCtx(),
        createMockStep('fill', {
          retry: { count: 3, intervalMs: 100, backoff: 'exp' },
          target: { candidates: [{ type: 'css', value: '#input' }] },
          value: 'test',
        }),
        1,
        { skipRetry: false },
      );

      expect(registryExecute).toHaveBeenCalledTimes(1);
      const [, action] = registryExecute.mock.calls[0];
      expect(action.policy?.retry).toBeDefined();
      expect(action.policy.retry.retries).toBe(3);
    });

    it('preserves action.policy.retry when skipRetry is not specified', async () => {
      const executor = createStepExecutor(mockRegistry);

      await executor(
        createMockExecCtx(),
        createMockStep('fill', {
          retry: { count: 2, intervalMs: 50 },
          target: { candidates: [{ type: 'css', value: '#input' }] },
          value: 'test',
        }),
        1,
        {}, // No skipRetry
      );

      const [, action] = registryExecute.mock.calls[0];
      expect(action.policy?.retry).toBeDefined();
    });
  });

  describe('skipNavWait flag', () => {
    it('sets ctx.execution.skipNavWait when skipNavWait is true', async () => {
      const executor = createStepExecutor(mockRegistry);

      await executor(
        createMockExecCtx(),
        createMockStep('click', {
          target: { candidates: [{ type: 'css', value: '#btn' }] },
        }),
        1,
        { skipNavWait: true },
      );

      expect(registryExecute).toHaveBeenCalledTimes(1);
      const [actionCtx] = registryExecute.mock.calls[0];
      expect(actionCtx.execution?.skipNavWait).toBe(true);
    });

    it('does not set ctx.execution when skipNavWait is false', async () => {
      const executor = createStepExecutor(mockRegistry);

      await executor(
        createMockExecCtx(),
        createMockStep('click', {
          target: { candidates: [{ type: 'css', value: '#btn' }] },
        }),
        1,
        { skipNavWait: false },
      );

      const [actionCtx] = registryExecute.mock.calls[0];
      expect(actionCtx.execution).toBeUndefined();
    });

    it('does not set ctx.execution when skipNavWait is not specified', async () => {
      const executor = createStepExecutor(mockRegistry);

      await executor(
        createMockExecCtx(),
        createMockStep('navigate', {
          url: 'https://example.com',
        }),
        1,
        {}, // No skipNavWait
      );

      const [actionCtx] = registryExecute.mock.calls[0];
      expect(actionCtx.execution).toBeUndefined();
    });
  });

  describe('combined flags', () => {
    it('applies both skipRetry and skipNavWait together', async () => {
      const executor = createStepExecutor(mockRegistry);

      await executor(
        createMockExecCtx(),
        createMockStep('click', {
          retry: { count: 5, intervalMs: 200 },
          target: { candidates: [{ type: 'css', value: '#btn' }] },
        }),
        1,
        { skipRetry: true, skipNavWait: true },
      );

      const [actionCtx, action] = registryExecute.mock.calls[0];
      expect(action.policy?.retry).toBeUndefined();
      expect(actionCtx.execution?.skipNavWait).toBe(true);
    });
  });
});
