import { describe, it, expect } from 'vitest';
import { MockAutomationLifecycleEmitter } from '../../../mocks/MockAutomationLifecycleEmitter';
import { OverlaySyncService } from 'apps/companion/main/automation/application/services/OverlaySyncService';
import type { AutomationEvent } from 'apps/companion/main/automation/application/ports/IAutomationEventPublisher';
import type { OverlayAction } from 'apps/companion/main/automation/application/ports/IOverlaySyncPort';

type RendererOverlayState =
  | { status: 'idle' }
  | { status: 'starting'; actionId: string }
  | { status: 'in-progress'; actionId: string }
  | { status: 'completed'; actionId: string }
  | { status: 'failed'; actionId: string };

class RecordingPublisher {
  public events: AutomationEvent[] = [];
  async publish(event: AutomationEvent): Promise<void> {
    this.events.push(event);
  }
}

function reduceEventsToRendererState(events: AutomationEvent[]): RendererOverlayState {
  let state: RendererOverlayState = { status: 'idle' };

  for (const ev of events) {
    if (!ev.actionId) continue;
    switch (ev.type) {
      case 'modal-opened':
      case 'panel-attached':
        state = { status: 'starting', actionId: ev.actionId };
        break;
      case 'action-started':
        state = { status: 'in-progress', actionId: ev.actionId };
        break;
      case 'action-complete':
        state = { status: 'completed', actionId: ev.actionId };
        break;
      case 'action-failed':
      case 'panel-missing':
        state = { status: 'failed', actionId: ev.actionId };
        break;
    }
  }

  return state;
}

describe('renderer overlay lifecycle integration', () => {
  it('tracks starting → in-progress → completed lifecycle for a hosted action', async () => {
    const emitter = new MockAutomationLifecycleEmitter();
    const publisher = new RecordingPublisher();
    const svc = new OverlaySyncService({
      lifecycleEmitter: emitter,
      publisher,
      logger: console as any,
      defaultTimeoutMs: 2_000,
    });

    const action: OverlayAction = {
      id: 'hosted-session',
      label: 'Starting hosted session',
    };

    const ackPromise = svc.startAction(action);
 
    expect(publisher.events[0]?.type).toBe('modal-opened');
    expect(publisher.events[0]?.actionId).toBe('hosted-session');
 
    await emitter.emit({
      type: 'panel-attached',
      actionId: 'hosted-session',
      timestamp: Date.now(),
      payload: { selector: '#gridpilot-overlay' },
    });
 
    await emitter.emit({
      type: 'action-started',
      actionId: 'hosted-session',
      timestamp: Date.now(),
    });
 
    const ack = await ackPromise;
    expect(ack.id).toBe('hosted-session');
    expect(ack.status).toBe('confirmed');
 
    await publisher.publish({
      type: 'panel-attached',
      actionId: 'hosted-session',
      timestamp: Date.now(),
      payload: { selector: '#gridpilot-overlay' },
    } as AutomationEvent);
 
    await publisher.publish({
      type: 'action-started',
      actionId: 'hosted-session',
      timestamp: Date.now(),
    } as AutomationEvent);
 
    await publisher.publish({
      type: 'action-complete',
      actionId: 'hosted-session',
      timestamp: Date.now(),
    } as AutomationEvent);
 
    const rendererState = reduceEventsToRendererState(publisher.events);
 
    expect(rendererState.status).toBe('completed');
    expect((rendererState as { actionId: string }).actionId).toBe('hosted-session');
  });

  it('ends in failed state when panel-missing is emitted', async () => {
    const emitter = new MockAutomationLifecycleEmitter();
    const publisher = new RecordingPublisher();
    const svc = new OverlaySyncService({
      lifecycleEmitter: emitter,
      publisher,
      logger: console as any,
      defaultTimeoutMs: 200,
    });

    const action: OverlayAction = {
      id: 'hosted-failure',
      label: 'Hosted session failing',
    };

    void svc.startAction(action);
 
    await publisher.publish({
      type: 'panel-attached',
      actionId: 'hosted-failure',
      timestamp: Date.now(),
      payload: { selector: '#gridpilot-overlay' },
    } as AutomationEvent);
 
    await publisher.publish({
      type: 'action-failed',
      actionId: 'hosted-failure',
      timestamp: Date.now(),
      payload: { reason: 'validation error' },
    } as AutomationEvent);
 
    const rendererState = reduceEventsToRendererState(publisher.events);
 
    expect(rendererState.status).toBe('failed');
    expect((rendererState as { actionId: string }).actionId).toBe('hosted-failure');
  });
});