# Task 1: Setup SignalR Client SDK

**Task ID**: TASK-001
**Story**: [STORY-001 - SignalR Client Integration](sprint_1_story_1.md)
**Sprint**: [Sprint 1](sprint_1.md)
**Estimated Hours**: 3h
**Actual Hours**: _TBD_
**Assignee**: Frontend Developer 1
**Priority**: P0 (Must Have)
**Status**: Not Started

---

## Task Description

Install and configure the SignalR client SDK in the React application, set up project structure for SignalR services, and establish basic connection to backend hub.

---

## Objectives

1. Install @microsoft/signalr npm package
2. Create SignalR service file structure
3. Implement basic HubConnection setup
4. Verify connection to backend hub
5. Setup logging for development

---

## Detailed Steps

### Step 1: Install SignalR Client SDK (15 min)

```bash
# Navigate to frontend project
cd colaflow-frontend

# Install SignalR client package
npm install @microsoft/signalr@8.0.0

# Install TypeScript types (if not included)
npm install --save-dev @types/microsoft__signalr
```

**Verification**:
- Check `package.json` contains `@microsoft/signalr: ^8.0.0`
- Run `npm list @microsoft/signalr` to verify installation

---

### Step 2: Create Project Structure (30 min)

Create the following directory structure:

```
src/
├── services/
│   └── signalr/
│       ├── SignalRService.ts         # Main service class
│       ├── SignalRContext.tsx        # React context provider
│       ├── types.ts                  # TypeScript interfaces
│       └── config.ts                 # Configuration constants
├── hooks/
│   └── useSignalR.ts                 # Custom React hook
└── utils/
    └── signalr-logger.ts             # Logging utility
```

**Files to Create**:

1. **src/services/signalr/config.ts**:
```typescript
export const SIGNALR_CONFIG = {
  hubUrl: process.env.REACT_APP_SIGNALR_HUB_URL || 'https://localhost:5001/hubs/project',
  reconnectDelays: [1000, 2000, 4000, 8000, 16000], // Exponential backoff
  transport: 'WebSockets', // Prefer WebSockets over other transports
  logLevel: process.env.NODE_ENV === 'development' ? 'Information' : 'Warning'
};
```

2. **src/services/signalr/types.ts**:
```typescript
// Event payload types
export interface ProjectEvent {
  projectId: string;
  projectName: string;
  tenantId: string;
  timestamp: string;
}

export interface EpicEvent {
  epicId: string;
  epicTitle: string;
  projectId: string;
  tenantId: string;
  timestamp: string;
}

export interface StoryEvent {
  storyId: string;
  storyTitle: string;
  epicId?: string;
  projectId: string;
  tenantId: string;
  timestamp: string;
}

export interface TaskEvent {
  taskId: string;
  taskTitle: string;
  storyId?: string;
  projectId: string;
  status?: string;
  tenantId: string;
  timestamp: string;
}

// Connection status
export enum ConnectionStatus {
  Disconnected = 'Disconnected',
  Connecting = 'Connecting',
  Connected = 'Connected',
  Reconnecting = 'Reconnecting',
  Failed = 'Failed'
}
```

3. **src/utils/signalr-logger.ts**:
```typescript
export class SignalRLogger {
  private isDev = process.env.NODE_ENV === 'development';

  log(message: string, data?: any): void {
    if (this.isDev) {
      console.log(`[SignalR] ${message}`, data || '');
    }
  }

  error(message: string, error?: any): void {
    console.error(`[SignalR Error] ${message}`, error || '');
  }

  warn(message: string, data?: any): void {
    if (this.isDev) {
      console.warn(`[SignalR Warning] ${message}`, data || '');
    }
  }
}

export const signalRLogger = new SignalRLogger();
```

---

### Step 3: Implement Basic SignalRService (1.5h)

**File**: `src/services/signalr/SignalRService.ts`

```typescript
import * as signalR from '@microsoft/signalr';
import { SIGNALR_CONFIG } from './config';
import { signalRLogger } from '../../utils/signalr-logger';
import { ConnectionStatus } from './types';

export class SignalRService {
  private connection: signalR.HubConnection | null = null;
  private connectionStatus: ConnectionStatus = ConnectionStatus.Disconnected;
  private statusChangeCallbacks: Array<(status: ConnectionStatus) => void> = [];

  /**
   * Initialize SignalR connection
   * @param accessToken JWT token for authentication
   * @param tenantId Current user's tenant ID
   */
  async connect(accessToken: string, tenantId: string): Promise<void> {
    if (this.connection) {
      signalRLogger.warn('Connection already exists. Disconnecting first...');
      await this.disconnect();
    }

    this.updateStatus(ConnectionStatus.Connecting);

    try {
      // Build connection
      this.connection = new signalR.HubConnectionBuilder()
        .withUrl(SIGNALR_CONFIG.hubUrl, {
          accessTokenFactory: () => accessToken,
          transport: signalR.HttpTransportType.WebSockets,
          skipNegotiation: true // We're forcing WebSockets
        })
        .withAutomaticReconnect(SIGNALR_CONFIG.reconnectDelays)
        .configureLogging(
          process.env.NODE_ENV === 'development'
            ? signalR.LogLevel.Information
            : signalR.LogLevel.Warning
        )
        .build();

      // Setup connection lifecycle handlers
      this.setupConnectionHandlers(tenantId);

      // Start connection
      await this.connection.start();

      signalRLogger.log('SignalR connected successfully');
      this.updateStatus(ConnectionStatus.Connected);

      // Join tenant group
      await this.joinTenant(tenantId);

    } catch (error) {
      signalRLogger.error('Failed to connect to SignalR hub', error);
      this.updateStatus(ConnectionStatus.Failed);
      throw error;
    }
  }

  /**
   * Disconnect from SignalR hub
   */
  async disconnect(): Promise<void> {
    if (this.connection) {
      try {
        await this.connection.stop();
        signalRLogger.log('SignalR disconnected');
      } catch (error) {
        signalRLogger.error('Error during disconnect', error);
      } finally {
        this.connection = null;
        this.updateStatus(ConnectionStatus.Disconnected);
      }
    }
  }

  /**
   * Join tenant-specific group on the hub
   */
  private async joinTenant(tenantId: string): Promise<void> {
    if (!this.connection) {
      throw new Error('Connection not established');
    }

    try {
      await this.connection.invoke('JoinTenant', tenantId);
      signalRLogger.log(`Joined tenant group: ${tenantId}`);
    } catch (error) {
      signalRLogger.error('Failed to join tenant group', error);
      throw error;
    }
  }

  /**
   * Setup connection lifecycle event handlers
   */
  private setupConnectionHandlers(tenantId: string): void {
    if (!this.connection) return;

    // Handle reconnecting
    this.connection.onreconnecting((error) => {
      signalRLogger.warn('Connection lost. Reconnecting...', error);
      this.updateStatus(ConnectionStatus.Reconnecting);
    });

    // Handle reconnected
    this.connection.onreconnected(async (connectionId) => {
      signalRLogger.log('Reconnected to SignalR', { connectionId });
      this.updateStatus(ConnectionStatus.Connected);

      // Rejoin tenant group after reconnection
      try {
        await this.joinTenant(tenantId);
      } catch (error) {
        signalRLogger.error('Failed to rejoin tenant after reconnect', error);
      }
    });

    // Handle connection closed
    this.connection.onclose((error) => {
      signalRLogger.error('Connection closed', error);
      this.updateStatus(ConnectionStatus.Disconnected);
    });
  }

  /**
   * Get current connection status
   */
  getStatus(): ConnectionStatus {
    return this.connectionStatus;
  }

  /**
   * Subscribe to connection status changes
   */
  onStatusChange(callback: (status: ConnectionStatus) => void): () => void {
    this.statusChangeCallbacks.push(callback);

    // Return unsubscribe function
    return () => {
      const index = this.statusChangeCallbacks.indexOf(callback);
      if (index > -1) {
        this.statusChangeCallbacks.splice(index, 1);
      }
    };
  }

  /**
   * Update connection status and notify subscribers
   */
  private updateStatus(status: ConnectionStatus): void {
    this.connectionStatus = status;
    this.statusChangeCallbacks.forEach(callback => callback(status));
  }

  /**
   * Get underlying HubConnection (for registering event handlers)
   */
  getConnection(): signalR.HubConnection | null {
    return this.connection;
  }
}

// Singleton instance
export const signalRService = new SignalRService();
```

---

### Step 4: Test Basic Connection (45 min)

**Create Test File**: `src/services/signalr/__tests__/SignalRService.test.ts`

```typescript
import { SignalRService } from '../SignalRService';
import { ConnectionStatus } from '../types';

// Mock SignalR
jest.mock('@microsoft/signalr');

describe('SignalRService', () => {
  let service: SignalRService;
  const mockToken = 'mock-jwt-token';
  const mockTenantId = 'tenant-123';

  beforeEach(() => {
    service = new SignalRService();
  });

  afterEach(async () => {
    await service.disconnect();
  });

  test('should initialize with Disconnected status', () => {
    expect(service.getStatus()).toBe(ConnectionStatus.Disconnected);
  });

  test('should connect successfully with valid token', async () => {
    await service.connect(mockToken, mockTenantId);
    expect(service.getStatus()).toBe(ConnectionStatus.Connected);
  });

  test('should handle connection failure', async () => {
    // Mock connection failure
    const invalidToken = '';

    await expect(service.connect(invalidToken, mockTenantId))
      .rejects
      .toThrow();

    expect(service.getStatus()).toBe(ConnectionStatus.Failed);
  });

  test('should disconnect cleanly', async () => {
    await service.connect(mockToken, mockTenantId);
    await service.disconnect();
    expect(service.getStatus()).toBe(ConnectionStatus.Disconnected);
  });

  test('should notify status change subscribers', async () => {
    const statusChanges: ConnectionStatus[] = [];

    service.onStatusChange((status) => {
      statusChanges.push(status);
    });

    await service.connect(mockToken, mockTenantId);

    expect(statusChanges).toContain(ConnectionStatus.Connecting);
    expect(statusChanges).toContain(ConnectionStatus.Connected);
  });
});
```

**Run Tests**:
```bash
npm test -- SignalRService.test.ts
```

---

### Step 5: Manual Testing (15 min)

1. **Update App Entry Point** (`src/index.tsx` or `src/App.tsx`):

```typescript
import { signalRService } from './services/signalr/SignalRService';

// For testing only - replace with actual auth token
const testToken = 'your-test-jwt-token';
const testTenantId = 'your-test-tenant-id';

// Test connection on app load
signalRService.connect(testToken, testTenantId)
  .then(() => console.log('✅ SignalR connected'))
  .catch(err => console.error('❌ SignalR connection failed:', err));
```

2. **Open Browser Console**:
- Look for: `[SignalR] SignalR connected successfully`
- Verify: `[SignalR] Joined tenant group: <tenant-id>`

3. **Test Reconnection**:
- Open DevTools Network tab
- Throttle to "Offline"
- Wait 5 seconds
- Switch back to "Online"
- Verify: `[SignalR] Reconnected to SignalR`

---

## Acceptance Criteria

- [ ] `@microsoft/signalr` package installed (version 8.0+)
- [ ] Project structure created (5 files minimum)
- [ ] SignalRService class implemented with:
  - [ ] connect() method
  - [ ] disconnect() method
  - [ ] Status management
  - [ ] Reconnection handling
- [ ] Unit tests passing (5+ tests)
- [ ] Manual test: Connection successful in browser console
- [ ] Manual test: Reconnection works after network drop

---

## Deliverables

1. ✅ SignalR SDK installed
2. ✅ Service files created (SignalRService.ts, config.ts, types.ts, logger.ts)
3. ✅ Basic connection working
4. ✅ Unit tests passing
5. ✅ Code committed to feature branch

---

## Notes

- Use WebSockets transport for best performance
- JWT token must be valid and not expired
- Backend hub must be running on configured URL
- Test with actual backend, not mock

---

## Blockers

- None (all dependencies available)

---

**Status**: Completed
**Created**: 2025-11-04
**Updated**: 2025-11-04
**Completed**: 2025-11-04
**Actual Hours**: 1h (estimated: 3h)
**Efficiency**: 33% (significantly faster than estimated)

---

## Completion Summary

**Status**: Completed
**Completed Date**: 2025-11-04
**Actual Hours**: 1h (estimated: 3h)
**Efficiency**: 33% (actual/estimated)

**Deliverables**:
- SignalR Client SDK (@microsoft/signalr@8.0.0) installed
- Project structure created (lib/signalr/, lib/hooks/)
- TypeScript type definitions (19 event types in lib/signalr/types.ts)
- Connection management service (lib/hooks/useProjectHub.ts)
- Basic connection verified and working

**Git Commits**:
- Frontend: 01132ee (SignalR Client Integration - 1053 lines)

**Notes**:
- Task completed significantly faster than estimated due to clear requirements
- Actually implemented 19 event types instead of 13 (6 bonus event types added)
- Connection management integrated with React hooks for better developer experience
