import { injectable } from 'tsyringe';
import { ExtractedEntities, TaskClassification } from '../nlp.service';
import { v4 as uuidv4 } from 'uuid';

/**
 * Structured task data interfaces
 */
export interface StructuredTask {
  id?: string;
  title: string;
  description: string;
  type: string;
  priority: 'critical' | 'high' | 'medium' | 'low';
  status: 'pending' | 'in-progress' | 'completed' | 'blocked';
  estimatedHours?: number;
  deadline?: Date;
  assignees: string[];
  dependencies: string[];
  tags: string[];
  acceptanceCriteria: string[];
  metadata: {
    urgency: string;
    complexity: string;
    sentiment: string;
    confidence: number;
    language: string;
    createdAt: Date;
    source: 'nlp';
  };
  projectId?: string;
  parentTaskId?: string;
}

/**
 * Validation rules for structured data
 */
const VALIDATION_RULES = {
  title: {
    minLength: 3,
    maxLength: 100,
  },
  description: {
    minLength: 10,
    maxLength: 5000,
  },
  tags: {
    maxCount: 20,
    maxLength: 30,
  },
  acceptanceCriteria: {
    maxCount: 20,
    maxLength: 500,
  },
};

/**
 * Structured Data Generator Service
 */
@injectable()
export class StructuredDataGeneratorService {
  /**
   * Generate structured task data from extracted entities and classification
   */
  async generate(input: {
    text: string;
    entities: ExtractedEntities;
    classification: TaskClassification;
    context?: Record<string, any>;
    projectId?: string;
  }): Promise<StructuredTask> {
    const { text, entities, classification, context, projectId } = input;

    // Generate or use existing ID
    const taskId = context?.taskId || uuidv4();

    // Build the structured task
    const structuredTask: StructuredTask = {
      id: taskId,
      title: this.generateTitle(entities, text),
      description: this.generateDescription(entities, text),
      type: classification.type || 'task',
      priority: this.normalizePriority(entities.priority, classification.urgency),
      status: this.determineInitialStatus(entities, context),
      estimatedHours: entities.estimatedHours,
      deadline: entities.deadline,
      assignees: this.normalizeAssignees(entities.assignees),
      dependencies: this.normalizeDependencies(entities.dependencies),
      tags: this.normalizeTags(entities.tags),
      acceptanceCriteria: this.normalizeAcceptanceCriteria(entities.acceptanceCriteria),
      metadata: {
        urgency: classification.urgency || 'normal',
        complexity: classification.complexity || 'moderate',
        sentiment: classification.sentiment || 'neutral',
        confidence: this.calculateOverallConfidence(entities.confidence, classification.confidence),
        language: context?.language || 'en',
        createdAt: new Date(),
        source: 'nlp',
      },
      projectId,
      parentTaskId: context?.parentTaskId,
    };

    // Validate the structured data
    this.validate(structuredTask);

    // Apply enrichments based on context
    const enriched = this.enrichWithContext(structuredTask, context);

    // Apply fallback values for missing data
    const finalized = this.applyFallbacks(enriched);

    return finalized;
  }

  /**
   * Generate a proper title
   */
  private generateTitle(entities: ExtractedEntities, text: string): string {
    if (entities.title) {
      return this.normalizeTitle(entities.title);
    }

    // Fallback: Use first sentence or truncated text
    const firstSentence = text.split(/[.!?]/)[0];
    const title = firstSentence.length <= VALIDATION_RULES.title.maxLength 
      ? firstSentence 
      : firstSentence.substring(0, VALIDATION_RULES.title.maxLength - 3) + '...';

    return this.normalizeTitle(title);
  }

  /**
   * Generate a proper description
   */
  private generateDescription(entities: ExtractedEntities, text: string): string {
    if (entities.description) {
      return this.normalizeDescription(entities.description);
    }

    // Use the full text as description if not too long
    if (text.length <= VALIDATION_RULES.description.maxLength) {
      return text;
    }

    // Truncate if too long
    return text.substring(0, VALIDATION_RULES.description.maxLength - 3) + '...';
  }

  /**
   * Normalize priority based on multiple signals
   */
  private normalizePriority(
    entityPriority?: ExtractedEntities['priority'],
    urgency?: TaskClassification['urgency']
  ): StructuredTask['priority'] {
    // If we have explicit priority, use it
    if (entityPriority) {
      return entityPriority;
    }

    // Map urgency to priority
    switch (urgency) {
      case 'immediate':
        return 'critical';
      case 'urgent':
        return 'high';
      case 'low':
        return 'low';
      default:
        return 'medium';
    }
  }

  /**
   * Determine initial task status
   */
  private determineInitialStatus(
    entities: ExtractedEntities,
    context?: Record<string, any>
  ): StructuredTask['status'] {
    // Check if context specifies status
    if (context?.status && this.isValidStatus(context.status)) {
      return context.status;
    }

    // Check for blocking dependencies
    if (entities.dependencies && entities.dependencies.length > 0) {
      // Would need to check if dependencies are completed
      // For now, assume blocked if has dependencies
      const hasUnresolvedDeps = context?.unresolvedDependencies?.length > 0;
      if (hasUnresolvedDeps) {
        return 'blocked';
      }
    }

    // Default to pending
    return 'pending';
  }

  /**
   * Normalize assignees list
   */
  private normalizeAssignees(assignees?: string[]): string[] {
    if (!assignees || assignees.length === 0) {
      return [];
    }

    return assignees
      .map(a => a.trim())
      .filter(a => a.length > 0)
      .slice(0, 10); // Limit to 10 assignees
  }

  /**
   * Normalize dependencies list
   */
  private normalizeDependencies(dependencies?: string[]): string[] {
    if (!dependencies || dependencies.length === 0) {
      return [];
    }

    return dependencies
      .map(d => d.trim())
      .filter(d => d.length > 0)
      .slice(0, 20); // Limit to 20 dependencies
  }

  /**
   * Normalize tags
   */
  private normalizeTags(tags?: string[]): string[] {
    if (!tags || tags.length === 0) {
      return [];
    }

    return tags
      .map(tag => tag.trim().toLowerCase())
      .filter(tag => tag.length > 0 && tag.length <= VALIDATION_RULES.tags.maxLength)
      .slice(0, VALIDATION_RULES.tags.maxCount);
  }

  /**
   * Normalize acceptance criteria
   */
  private normalizeAcceptanceCriteria(criteria?: string[]): string[] {
    if (!criteria || criteria.length === 0) {
      return [];
    }

    return criteria
      .map(c => c.trim())
      .filter(c => c.length > 0 && c.length <= VALIDATION_RULES.acceptanceCriteria.maxLength)
      .slice(0, VALIDATION_RULES.acceptanceCriteria.maxCount);
  }

  /**
   * Normalize title string
   */
  private normalizeTitle(title: string): string {
    // Remove excessive whitespace
    let normalized = title.trim().replace(/\s+/g, ' ');
    
    // Capitalize first letter
    if (normalized.length > 0) {
      normalized = normalized[0].toUpperCase() + normalized.slice(1);
    }

    // Ensure within length limits
    if (normalized.length < VALIDATION_RULES.title.minLength) {
      normalized = normalized.padEnd(VALIDATION_RULES.title.minLength, '.');
    } else if (normalized.length > VALIDATION_RULES.title.maxLength) {
      normalized = normalized.substring(0, VALIDATION_RULES.title.maxLength - 3) + '...';
    }

    return normalized;
  }

  /**
   * Normalize description string
   */
  private normalizeDescription(description: string): string {
    // Remove excessive whitespace but preserve paragraph breaks
    let normalized = description.trim().replace(/\n\s*\n/g, '\n\n').replace(/ +/g, ' ');

    // Ensure within length limits
    if (normalized.length < VALIDATION_RULES.description.minLength) {
      normalized = normalized + '\n\n[Additional details to be provided]';
    } else if (normalized.length > VALIDATION_RULES.description.maxLength) {
      normalized = normalized.substring(0, VALIDATION_RULES.description.maxLength - 3) + '...';
    }

    return normalized;
  }

  /**
   * Calculate overall confidence
   */
  private calculateOverallConfidence(
    entityConfidence?: number,
    classificationConfidence?: number
  ): number {
    const entity = entityConfidence || 0;
    const classification = classificationConfidence || 0;
    
    // Weighted average
    return (entity * 0.6 + classification * 0.4);
  }

  /**
   * Validate structured task data
   */
  private validate(task: StructuredTask): void {
    const errors: string[] = [];

    // Validate title
    if (!task.title || task.title.length < VALIDATION_RULES.title.minLength) {
      errors.push(`Title must be at least ${VALIDATION_RULES.title.minLength} characters`);
    }
    if (task.title.length > VALIDATION_RULES.title.maxLength) {
      errors.push(`Title must be at most ${VALIDATION_RULES.title.maxLength} characters`);
    }

    // Validate description
    if (!task.description || task.description.length < VALIDATION_RULES.description.minLength) {
      errors.push(`Description must be at least ${VALIDATION_RULES.description.minLength} characters`);
    }
    if (task.description.length > VALIDATION_RULES.description.maxLength) {
      errors.push(`Description must be at most ${VALIDATION_RULES.description.maxLength} characters`);
    }

    // Validate arrays
    if (task.tags.length > VALIDATION_RULES.tags.maxCount) {
      errors.push(`Too many tags (max ${VALIDATION_RULES.tags.maxCount})`);
    }
    if (task.acceptanceCriteria.length > VALIDATION_RULES.acceptanceCriteria.maxCount) {
      errors.push(`Too many acceptance criteria (max ${VALIDATION_RULES.acceptanceCriteria.maxCount})`);
    }

    if (errors.length > 0) {
      console.warn('Validation warnings:', errors);
      // Don't throw, just log warnings - we'll handle with fallbacks
    }
  }

  /**
   * Enrich task with context information
   */
  private enrichWithContext(
    task: StructuredTask,
    context?: Record<string, any>
  ): StructuredTask {
    if (!context) return task;

    // Add project context
    if (context.project) {
      task.projectId = task.projectId || context.project.id;
      
      // Add project tag if not present
      if (context.project.name && !task.tags.includes(context.project.name.toLowerCase())) {
        task.tags.push(context.project.name.toLowerCase());
      }
    }

    // Add user context
    if (context.user) {
      // If no assignees and user is creating, auto-assign
      if (task.assignees.length === 0 && context.autoAssign) {
        task.assignees.push(context.user.username || context.user.id);
      }
    }

    // Add team context
    if (context.team && Array.isArray(context.team)) {
      // Check if any team members are mentioned but not assigned
      context.team.forEach((member: any) => {
        if (task.description.includes(member.name) && !task.assignees.includes(member.username)) {
          task.assignees.push(member.username);
        }
      });
    }

    // Add sprint/iteration context
    if (context.sprint) {
      task.metadata = {
        ...task.metadata,
        sprint: context.sprint.id,
        iteration: context.sprint.iteration,
      } as any;
    }

    return task;
  }

  /**
   * Apply fallback values for missing required data
   */
  private applyFallbacks(task: StructuredTask): StructuredTask {
    // Ensure title
    if (!task.title || task.title.trim().length < VALIDATION_RULES.title.minLength) {
      task.title = 'Untitled Task';
    }

    // Ensure description
    if (!task.description || task.description.trim().length < VALIDATION_RULES.description.minLength) {
      task.description = task.title + '\n\n[Description to be provided]';
    }

    // Ensure at least one tag
    if (task.tags.length === 0) {
      task.tags.push(task.type);
      task.tags.push(task.priority);
    }

    // Add NLP tag to indicate source
    if (!task.tags.includes('nlp-generated')) {
      task.tags.push('nlp-generated');
    }

    return task;
  }

  /**
   * Check if status is valid
   */
  private isValidStatus(status: string): status is StructuredTask['status'] {
    return ['pending', 'in-progress', 'completed', 'blocked'].includes(status);
  }
}