import {
  ProductRoadmap,
  RoadmapTheme,
  Initiative,
  Feature,
  Milestone,
  Release,
  TimelineView,
  TimelineItem
} from './types.js';

export class TimelineGenerator {
  
  generateQuarterlyView(
    roadmap: ProductRoadmap,
    startQuarter: string,
    endQuarter: string
  ): TimelineView {
    const items: TimelineItem[] = [];
    const startDate = this.parseQuarter(startQuarter);
    const endDate = this.parseQuarter(endQuarter);
    
    // Add themes
    for (const theme of roadmap.themes) {
      const themeStart = this.parseQuarter(theme.timeframe.startQuarter);
      const themeEnd = this.parseQuarter(theme.timeframe.endQuarter);
      
      // Only include themes that overlap with the view period
      if (themeEnd >= startDate && themeStart <= endDate) {
        items.push({
          id: theme.id,
          type: 'theme',
          name: theme.name,
          startDate: themeStart,
          endDate: themeEnd,
          status: theme.status,
          dependencies: [],
          progress: theme.metrics.progressPercentage
        });
      }
    }
    
    // Add milestones
    for (const milestone of roadmap.milestones) {
      if (milestone.date >= startDate && milestone.date <= endDate) {
        items.push({
          id: milestone.id,
          type: 'milestone',
          name: milestone.name,
          startDate: milestone.date,
          endDate: milestone.date,
          status: milestone.status,
          dependencies: milestone.dependencies
        });
      }
    }
    
    // Sort by start date
    items.sort((a, b) => a.startDate.getTime() - b.startDate.getTime());
    
    return {
      type: 'quarterly',
      startDate,
      endDate,
      items
    };
  }
  
  generateMonthlyView(
    roadmap: ProductRoadmap,
    initiatives: Initiative[],
    features: Feature[],
    startMonth: Date,
    months: number
  ): TimelineView {
    const items: TimelineItem[] = [];
    const endDate = new Date(startMonth);
    endDate.setMonth(endDate.getMonth() + months);
    
    // Add initiatives with estimated timelines
    for (const initiative of initiatives) {
      const estimatedStart = this.estimateInitiativeStart(initiative, roadmap);
      const estimatedEnd = this.estimateInitiativeEnd(initiative, estimatedStart);
      
      if (estimatedEnd >= startMonth && estimatedStart <= endDate) {
        items.push({
          id: initiative.id,
          type: 'initiative',
          name: initiative.title,
          startDate: estimatedStart,
          endDate: estimatedEnd,
          status: initiative.status,
          dependencies: initiative.dependencies.map(d => d.targetId || '').filter(Boolean),
          progress: this.calculateInitiativeProgress(initiative, features)
        });
      }
    }
    
    // Add features
    for (const feature of features) {
      const featureStart = this.estimateFeatureStart(feature, initiatives);
      const featureEnd = this.estimateFeatureEnd(feature, featureStart);
      
      if (featureEnd >= startMonth && featureStart <= endDate) {
        items.push({
          id: feature.id,
          type: 'feature',
          name: feature.name,
          startDate: featureStart,
          endDate: featureEnd,
          status: feature.status,
          dependencies: [],
          progress: feature.status === 'completed' ? 100 : 
                   feature.status === 'in-progress' ? 50 : 0
        });
      }
    }
    
    // Add releases
    for (const release of roadmap.releases) {
      if (release.date >= startMonth && release.date <= endDate) {
        items.push({
          id: release.id,
          type: 'release',
          name: `v${release.version}: ${release.name}`,
          startDate: release.date,
          endDate: release.date,
          status: release.status,
          dependencies: []
        });
      }
    }
    
    // Sort by start date
    items.sort((a, b) => a.startDate.getTime() - b.startDate.getTime());
    
    return {
      type: 'monthly',
      startDate: startMonth,
      endDate,
      items
    };
  }
  
  generateReleaseView(
    roadmap: ProductRoadmap,
    features: Map<string, Feature>
  ): TimelineView {
    const items: TimelineItem[] = [];
    const now = new Date();
    
    // Group features by release
    const releaseGroups = new Map<string, Feature[]>();
    for (const [_, feature] of features) {
      if (feature.targetRelease) {
        if (!releaseGroups.has(feature.targetRelease)) {
          releaseGroups.set(feature.targetRelease, []);
        }
        releaseGroups.get(feature.targetRelease)!.push(feature);
      }
    }
    
    // Add releases with their features
    for (const release of roadmap.releases) {
      items.push({
        id: release.id,
        type: 'release',
        name: `v${release.version}: ${release.name}`,
        startDate: release.date,
        endDate: release.date,
        status: release.status,
        dependencies: [],
        progress: this.calculateReleaseProgress(release, releaseGroups.get(release.id) || [])
      });
      
      // Add features for this release
      const releaseFeatures = releaseGroups.get(release.id) || [];
      for (const feature of releaseFeatures) {
        const featureStart = new Date(release.date);
        featureStart.setMonth(featureStart.getMonth() - 3); // Assume 3 months development
        
        items.push({
          id: feature.id,
          type: 'feature',
          name: feature.name,
          startDate: featureStart,
          endDate: release.date,
          status: feature.status,
          dependencies: [],
          progress: feature.status === 'completed' ? 100 : 
                   feature.status === 'in-progress' ? 50 : 0
        });
      }
    }
    
    // Sort by date
    items.sort((a, b) => a.startDate.getTime() - b.startDate.getTime());
    
    const startDate = items[0]?.startDate || now;
    const endDate = items[items.length - 1]?.endDate || now;
    
    return {
      type: 'release',
      startDate,
      endDate,
      items
    };
  }
  
  generateNowNextLaterView(
    roadmap: ProductRoadmap,
    themes: Map<string, RoadmapTheme>,
    initiatives: Map<string, Initiative>,
    features: Map<string, Feature>
  ): TimelineView {
    const now = new Date();
    const nextQuarter = new Date(now);
    nextQuarter.setMonth(nextQuarter.getMonth() + 3);
    const laterDate = new Date(now);
    laterDate.setMonth(laterDate.getMonth() + 9);
    
    const items: TimelineItem[] = [];
    
    // Categorize items into Now, Next, Later
    const categorizeByTiming = (startDate: Date) => {
      if (startDate <= now) return 'now';
      if (startDate <= nextQuarter) return 'next';
      return 'later';
    };
    
    // Process themes
    for (const theme of roadmap.themes) {
      const themeStart = this.parseQuarter(theme.timeframe.startQuarter);
      const timing = categorizeByTiming(themeStart);
      
      items.push({
        id: theme.id,
        type: 'theme',
        name: `[${timing.toUpperCase()}] ${theme.name}`,
        startDate: timing === 'now' ? now : 
                  timing === 'next' ? nextQuarter : laterDate,
        endDate: this.parseQuarter(theme.timeframe.endQuarter),
        status: theme.status,
        dependencies: [],
        progress: theme.metrics.progressPercentage
      });
    }
    
    // Process initiatives
    for (const [_, initiative] of initiatives) {
      const initiativeStart = this.estimateInitiativeStart(initiative, roadmap);
      const timing = categorizeByTiming(initiativeStart);
      
      items.push({
        id: initiative.id,
        type: 'initiative',
        name: `[${timing.toUpperCase()}] ${initiative.title}`,
        startDate: timing === 'now' ? now : 
                  timing === 'next' ? nextQuarter : laterDate,
        endDate: this.estimateInitiativeEnd(initiative, initiativeStart),
        status: initiative.status,
        dependencies: initiative.dependencies.map(d => d.targetId || '').filter(Boolean),
        progress: 0
      });
    }
    
    // Group by timing for better visualization
    items.sort((a, b) => {
      // First sort by timing category
      const aCategory = a.name.startsWith('[NOW]') ? 0 : 
                       a.name.startsWith('[NEXT]') ? 1 : 2;
      const bCategory = b.name.startsWith('[NOW]') ? 0 : 
                       b.name.startsWith('[NEXT]') ? 1 : 2;
      
      if (aCategory !== bCategory) return aCategory - bCategory;
      
      // Then by type (themes first, then initiatives, then features)
      const typeOrder = { theme: 0, initiative: 1, feature: 2, milestone: 3, release: 4 };
      return typeOrder[a.type] - typeOrder[b.type];
    });
    
    return {
      type: 'now-next-later',
      startDate: now,
      endDate: laterDate,
      items
    };
  }
  
  // Helper methods
  private parseQuarter(quarter: string): Date {
    const [q, year] = quarter.split(' ');
    const quarterNum = parseInt(q.substring(1));
    const yearNum = parseInt(year);
    const month = (quarterNum - 1) * 3;
    return new Date(yearNum, month, 1);
  }
  
  private estimateInitiativeStart(initiative: Initiative, roadmap: ProductRoadmap): Date {
    // Find the theme this initiative belongs to
    const theme = roadmap.themes.find(t => 
      t.initiatives.some(i => 
        (typeof i === 'string' ? i : i.id) === initiative.id
      )
    );
    
    if (theme) {
      return this.parseQuarter(theme.timeframe.startQuarter);
    }
    
    // Default to current date
    return new Date();
  }
  
  private estimateInitiativeEnd(initiative: Initiative, startDate: Date): Date {
    const endDate = new Date(startDate);
    
    // Estimate based on effort
    const totalWeeks = initiative.effort.developmentWeeks + 
                      initiative.effort.designWeeks + 
                      initiative.effort.qaWeeks;
    
    endDate.setDate(endDate.getDate() + (totalWeeks * 7));
    
    return endDate;
  }
  
  private estimateFeatureStart(feature: Feature, initiatives: Initiative[]): Date {
    // Find the initiative this feature belongs to
    const initiative = initiatives.find(i => 
      i.features.some(f => 
        (typeof f === 'string' ? f : f.id) === feature.id
      )
    );
    
    if (initiative) {
      // Return current date as we don't have the full roadmap context here
      return new Date();
    }
    
    return new Date();
  }
  
  private estimateFeatureEnd(feature: Feature, startDate: Date): Date {
    const endDate = new Date(startDate);
    
    // Estimate based on complexity
    const complexityWeeks = {
      'low': 2,
      'medium': 4,
      'high': 8,
      'very-high': 12
    };
    
    const weeks = complexityWeeks[feature.technicalComplexity];
    endDate.setDate(endDate.getDate() + (weeks * 7));
    
    return endDate;
  }
  
  private calculateInitiativeProgress(initiative: Initiative, features: Feature[]): number {
    const initiativeFeatures = features.filter(f => 
      initiative.features.some(initiativeFeature => 
        (typeof initiativeFeature === 'string' ? initiativeFeature : initiativeFeature.id) === f.id
      )
    );
    
    if (initiativeFeatures.length === 0) return 0;
    
    const completedCount = initiativeFeatures.filter(f => f.status === 'completed').length;
    return Math.round((completedCount / initiativeFeatures.length) * 100);
  }
  
  private calculateReleaseProgress(release: Release, features: Feature[]): number {
    if (features.length === 0) return 0;
    
    const completedCount = features.filter(f => f.status === 'completed').length;
    return Math.round((completedCount / features.length) * 100);
  }
  
  // Generate Gantt chart data
  generateGanttData(timelineView: TimelineView): any {
    return {
      tasks: timelineView.items.map((item, index) => ({
        id: item.id,
        name: item.name,
        start: item.startDate.toISOString(),
        end: item.endDate.toISOString(),
        progress: item.progress || 0,
        dependencies: item.dependencies,
        type: item.type,
        status: item.status,
        row: index
      })),
      viewStart: timelineView.startDate.toISOString(),
      viewEnd: timelineView.endDate.toISOString()
    };
  }
  
  // Critical path analysis
  findCriticalPath(items: TimelineItem[]): string[] {
    // Build dependency graph
    const graph = new Map<string, Set<string>>();
    const itemMap = new Map<string, TimelineItem>();
    
    for (const item of items) {
      itemMap.set(item.id, item);
      graph.set(item.id, new Set(item.dependencies));
    }
    
    // Find items with no dependencies (start nodes)
    const startNodes = items.filter(item => item.dependencies.length === 0);
    
    // Calculate earliest start/finish times
    const earliestTimes = new Map<string, { start: number, finish: number }>();
    
    const calculateEarliestTimes = (itemId: string): { start: number, finish: number } => {
      if (earliestTimes.has(itemId)) {
        return earliestTimes.get(itemId)!;
      }
      
      const item = itemMap.get(itemId)!;
      const dependencies = Array.from(graph.get(itemId) || []);
      
      let earliestStart = item.startDate.getTime();
      
      if (dependencies.length > 0) {
        const depFinishTimes = dependencies.map(depId => {
          const depTimes = calculateEarliestTimes(depId);
          return depTimes.finish;
        });
        earliestStart = Math.max(...depFinishTimes);
      }
      
      const duration = item.endDate.getTime() - item.startDate.getTime();
      const earliestFinish = earliestStart + duration;
      
      earliestTimes.set(itemId, { start: earliestStart, finish: earliestFinish });
      return { start: earliestStart, finish: earliestFinish };
    };
    
    // Calculate for all items
    for (const item of items) {
      calculateEarliestTimes(item.id);
    }
    
    // Find the critical path (items with no slack)
    const criticalPath: string[] = [];
    
    // Implementation simplified - in production would need full CPM algorithm
    // For now, return the longest dependency chain
    let maxDuration = 0;
    let longestPath: string[] = [];
    
    const findLongestPath = (itemId: string, currentPath: string[], currentDuration: number) => {
      currentPath.push(itemId);
      const item = itemMap.get(itemId)!;
      currentDuration += item.endDate.getTime() - item.startDate.getTime();
      
      const dependents = items.filter(i => i.dependencies.includes(itemId));
      
      if (dependents.length === 0) {
        if (currentDuration > maxDuration) {
          maxDuration = currentDuration;
          longestPath = [...currentPath];
        }
      } else {
        for (const dependent of dependents) {
          findLongestPath(dependent.id, [...currentPath], currentDuration);
        }
      }
    };
    
    for (const startNode of startNodes) {
      findLongestPath(startNode.id, [], 0);
    }
    
    return longestPath;
  }
}