/**
 * Hello Poll - Minimal host-app example
 * 
 * Demonstrates the complete polling flow:
 * 1. Define schemas with Zod
 * 2. Configure generic polling request
 * 3. Schedule with platform wrapper
 * 4. Handle delivery via outbox → dispatcher → acknowledge → CAS watermark
 */

import { 
  GenericPollingRequest, 
  PollingScheduleConfig,
  PollingResult,
  StarredProjectsRequest,
  StarredProjectsResponse
} from '@timesafari/polling-contracts';
import { 
  // StarredProjectsRequestSchema,
  StarredProjectsResponseSchema,
  createResponseValidator,
  generateIdempotencyKey
} from '@timesafari/polling-contracts';

// Mock server for testing
class MockServer {
  private data: Record<string, unknown>[] = [
    {
      planSummary: {
        jwtId: '1704067200_abc123_def45678',
        handleId: 'hello_project',
        name: 'Hello Project',
        description: 'A simple test project',
        issuerDid: 'did:key:test_issuer',
        agentDid: 'did:key:test_agent',
        startTime: '2025-01-01T00:00:00Z',
        endTime: '2025-01-31T23:59:59Z',
        version: '1.0.0'
      }
    }
  ];
  
  async handleRequest(request: StarredProjectsRequest): Promise<StarredProjectsResponse> {
    // Simulate API delay
    await new Promise(resolve => setTimeout(resolve, 100));
    
    // Filter data based on afterId
    let filteredData = this.data;
    if (request.afterId) {
      filteredData = this.data.filter(item => 
        item.planSummary.jwtId > (request.afterId || '')
      );
    }
    
    return {
      data: filteredData,
      hitLimit: false,
      pagination: {
        hasMore: false,
        nextAfterId: null
      }
    };
  }
  
  addNewData(jwtId: string, name: string): void {
    this.data.push({
      planSummary: {
        jwtId,
        handleId: `project_${jwtId.split('_')[0]}`,
        name,
        description: `Updated project: ${name}`,
        issuerDid: 'did:key:test_issuer',
        agentDid: 'did:key:test_agent',
        startTime: '2025-01-01T00:00:00Z',
        endTime: '2025-01-31T23:59:59Z',
        version: '1.0.0'
      }
    });
  }
}

// Mock storage adapter
class MockStorageAdapter {
  private storage = new Map<string, unknown>();
  
  async get(key: string): Promise<unknown> {
    return this.storage.get(key);
  }
  
  async set(key: string, value: unknown): Promise<void> {
    this.storage.set(key, value);
  }
  
  async delete(key: string): Promise<void> {
    this.storage.delete(key);
  }
  
  async exists(key: string): Promise<boolean> {
    return this.storage.has(key);
  }
}

// Mock authentication manager
class MockAuthManager {
  private token = 'mock_jwt_token';
  
  async getCurrentToken(): Promise<string | null> {
    return this.token;
  }
  
  async refreshToken(): Promise<string> {
    this.token = `mock_jwt_token_${Date.now()}`;
    return this.token;
  }
  
  async validateToken(token: string): Promise<boolean> {
    return token.startsWith('mock_jwt_token');
  }
}

// Mock polling manager
class MockPollingManager {
  private server: MockServer;
  private storage: MockStorageAdapter;
  private auth: MockAuthManager;
  
  constructor(server: MockServer, storage: MockStorageAdapter, auth: MockAuthManager) {
    this.server = server;
    this.storage = storage;
    this.auth = auth;
  }
  
  async executePoll<TRequest, TResponse>(
    request: GenericPollingRequest<TRequest, TResponse>
  ): Promise<PollingResult<TResponse>> {
    try {
      // Validate idempotency key
      if (!request.idempotencyKey) {
        request.idempotencyKey = generateIdempotencyKey();
      }
      
      // Execute request
      const response = await this.server.handleRequest(request.body as StarredProjectsRequest);
      
      // Validate response
      const validator = createResponseValidator(StarredProjectsResponseSchema);
      if (!validator.validate(response)) {
        throw new Error('Response validation failed');
      }
      
      return {
        success: true,
        data: response as TResponse,
        error: undefined,
        metadata: {
          requestId: request.idempotencyKey || 'unknown',
          timestamp: new Date().toISOString(),
          duration: 100,
          retryCount: 0
        }
      };
      
    } catch (error) {
      return {
        success: false,
        data: undefined,
        error: {
          code: 'EXECUTION_ERROR',
          message: String(error),
          retryable: true
        },
        metadata: {
          requestId: request.idempotencyKey || 'unknown',
          timestamp: new Date().toISOString(),
          duration: 0,
          retryCount: 0
        }
      };
    }
  }
  
  async schedulePoll<TRequest, TResponse>(
    config: PollingScheduleConfig<TRequest, TResponse>
  ): Promise<string> {
    const scheduleId = `schedule_${Date.now()}`;
    
    // Store configuration
    await this.storage.set(`polling_config_${scheduleId}`, config);
    
    // Simulate scheduling
    // eslint-disable-next-line no-console
    console.log(`Scheduled poll: ${scheduleId}`);
    
    return scheduleId;
  }
}

// Main example
async function runHelloPollExample(): Promise<void> {
  // eslint-disable-next-line no-console
  console.log('🚀 Starting Hello Poll Example');
  
  // 1. Set up dependencies
  const server = new MockServer();
  const storage = new MockStorageAdapter();
  const auth = new MockAuthManager();
  const pollingManager = new MockPollingManager(server, storage, auth);
  
  // 2. Define polling request
  const request: GenericPollingRequest<StarredProjectsRequest, StarredProjectsResponse> = {
    endpoint: '/api/v2/report/plansLastUpdatedBetween',
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'User-Agent': 'HelloPoll-Example/1.0.0'
    },
    body: {
      planIds: ['hello_project'],
      afterId: undefined, // Will be populated from watermark
      limit: 100
    },
    responseSchema: createResponseValidator(StarredProjectsResponseSchema),
    retryConfig: {
      maxAttempts: 3,
      backoffStrategy: 'exponential',
      baseDelayMs: 1000
    },
    timeoutMs: 30000
  };
  
  // 3. Schedule polling
  const scheduleConfig: PollingScheduleConfig<StarredProjectsRequest, StarredProjectsResponse> = {
    request,
    schedule: {
      cronExpression: '0 10,16 * * *', // 10 AM and 4 PM daily
      timezone: 'UTC',
      maxConcurrentPolls: 1
    },
    notificationConfig: {
      enabled: true,
      templates: {
        singleUpdate: '{projectName} has been updated',
        multipleUpdates: 'You have {count} new updates in your starred projects'
      },
      groupingRules: {
        maxGroupSize: 5,
        timeWindowMinutes: 5
      }
    },
    stateConfig: {
      watermarkKey: 'lastAckedStarredPlanChangesJwtId',
      storageAdapter: storage
    }
  };
  
  const scheduleId = await pollingManager.schedulePoll(scheduleConfig);
  // eslint-disable-next-line no-console
  console.log(`✅ Scheduled poll: ${scheduleId}`);
  
  // 4. Execute initial poll
  // eslint-disable-next-line no-console
  console.log('📡 Executing initial poll...');
  const result = await pollingManager.executePoll(request);
  
  if (result.success && result.data) {
    // eslint-disable-next-line no-console
    console.log(`✅ Found ${result.data.data.length} changes`);
    
    if (result.data.data.length > 0) {
      // 5. Generate notifications
      const changes = result.data.data;
      // eslint-disable-next-line no-console
      console.log('🔔 Generating notifications...');
      
      if (changes.length === 1) {
        const project = changes[0].planSummary;
        // eslint-disable-next-line no-console
        console.log(`📱 Notification: "${project.name} has been updated"`);
      } else {
        // eslint-disable-next-line no-console
        console.log(`📱 Notification: "You have ${changes.length} new updates in your starred projects"`);
      }
      
      // 6. Update watermark with CAS
      const latestJwtId = changes[changes.length - 1].planSummary.jwtId;
      await storage.set('lastAckedStarredPlanChangesJwtId', latestJwtId);
      // eslint-disable-next-line no-console
      console.log(`💾 Updated watermark: ${latestJwtId}`);
      
      // 7. Acknowledge changes (simulate)
      // eslint-disable-next-line no-console
      console.log('✅ Acknowledged changes with server');
    }
  } else {
    // eslint-disable-next-line no-console
    console.log('❌ Poll failed:', result.error?.message);
  }
  
  // 8. Simulate new data and poll again
  // eslint-disable-next-line no-console
  console.log('\n🔄 Adding new data and polling again...');
  server.addNewData('1704153600_new123_0badf00d', 'Updated Hello Project');
  
  // Update request with watermark
  request.body.afterId = await storage.get('lastAckedStarredPlanChangesJwtId');
  
  const result2 = await pollingManager.executePoll(request);
  
  if (result2.success && result2.data) {
    // eslint-disable-next-line no-console
    console.log(`✅ Found ${result2.data.data.length} new changes`);
    
    if (result2.data.data.length > 0) {
      const project = result2.data.data[0].planSummary;
      // eslint-disable-next-line no-console
      console.log(`📱 New notification: "${project.name} has been updated"`);
      
      // Update watermark
      const latestJwtId = result2.data.data[result2.data.data.length - 1].planSummary.jwtId;
      await storage.set('lastAckedStarredPlanChangesJwtId', latestJwtId);
      // eslint-disable-next-line no-console
      console.log(`💾 Updated watermark: ${latestJwtId}`);
    }
  }
  
  // eslint-disable-next-line no-console
  console.log('\n🎉 Hello Poll Example completed successfully!');
}

// Run the example
if (require.main === module) {
  runHelloPollExample().catch(// eslint-disable-next-line no-console
    console.error);
}

export { runHelloPollExample };
