import { ConfigManager } from '../../config/config-manager.js';
import { 
  Issue, 
  Comment, 
  Attachment, 
  IssueFilter,
  IssueStats,
  IssueTemplate,
  Milestone
} from './types.js';
import { promises as fs } from 'fs';
import path from 'path';

export class IssueTracker {
  private configManager: ConfigManager;
  private issuesPath: string;
  private templatesPath: string;
  private milestonesPath: string;

  constructor(configManager: ConfigManager) {
    this.configManager = configManager;
    this.issuesPath = '';
    this.templatesPath = '';
    this.milestonesPath = '';
  }

  async init(): Promise<void> {
    const storageManager = this.configManager.getStorageManager();
    const location = await storageManager.getStorageLocation();
    const dataPath = path.join(location.data, 'issue-tracking');
    
    this.issuesPath = path.join(dataPath, 'issues');
    this.templatesPath = path.join(dataPath, 'templates');
    this.milestonesPath = path.join(dataPath, 'milestones');
    
    await this.ensureDirectories();
    await this.createDefaultTemplates();
  }

  private async ensureDirectories(): Promise<void> {
    await fs.mkdir(this.issuesPath, { recursive: true });
    await fs.mkdir(this.templatesPath, { recursive: true });
    await fs.mkdir(this.milestonesPath, { recursive: true });
  }

  async createIssue(data: {
    type: Issue['type'];
    title: string;
    description: string;
    priority: Issue['priority'];
    createdBy: string;
    labels?: string[];
    affectedModules?: string[];
    assignedTo?: string;
  }): Promise<Issue> {
    const issue: Issue = {
      id: this.generateId(),
      type: data.type,
      title: data.title,
      description: data.description,
      status: 'open',
      priority: data.priority,
      createdAt: new Date().toISOString(),
      createdBy: data.createdBy,
      updatedAt: new Date().toISOString(),
      assignedTo: data.assignedTo,
      labels: data.labels || [],
      affectedModules: data.affectedModules || [],
      relatedIssues: [],
      attachments: [],
      comments: []
    };

    await this.saveIssue(issue);
    return issue;
  }

  async updateIssue(issueId: string, updates: Partial<Issue>): Promise<Issue> {
    const issue = await this.getIssue(issueId);
    
    const updatedIssue = {
      ...issue,
      ...updates,
      id: issue.id,
      createdAt: issue.createdAt,
      createdBy: issue.createdBy,
      updatedAt: new Date().toISOString()
    };

    if (updates.status === 'resolved' || updates.status === 'closed') {
      updatedIssue.closedAt = new Date().toISOString();
    }

    await this.saveIssue(updatedIssue);
    return updatedIssue;
  }

  async addComment(issueId: string, data: {
    author: string;
    content: string;
    type?: Comment['type'];
  }): Promise<Comment> {
    const issue = await this.getIssue(issueId);
    
    const comment: Comment = {
      id: this.generateId(),
      issueId,
      author: data.author,
      content: data.content,
      createdAt: new Date().toISOString(),
      type: data.type || 'comment'
    };

    issue.comments.push(comment);
    issue.updatedAt = new Date().toISOString();
    
    await this.saveIssue(issue);
    return comment;
  }

  async searchIssues(filter: IssueFilter): Promise<Issue[]> {
    const allIssues = await this.getAllIssues();
    
    return allIssues.filter(issue => {
      if (filter.type && !filter.type.includes(issue.type)) return false;
      if (filter.status && !filter.status.includes(issue.status)) return false;
      if (filter.priority && !filter.priority.includes(issue.priority)) return false;
      if (filter.assignedTo && issue.assignedTo !== filter.assignedTo) return false;
      if (filter.createdBy && issue.createdBy !== filter.createdBy) return false;
      
      if (filter.labels && filter.labels.length > 0) {
        const hasAllLabels = filter.labels.every(label => issue.labels.includes(label));
        if (!hasAllLabels) return false;
      }
      
      if (filter.affectedModules && filter.affectedModules.length > 0) {
        const hasModule = filter.affectedModules.some(module => 
          issue.affectedModules.includes(module)
        );
        if (!hasModule) return false;
      }
      
      if (filter.dateRange) {
        const createdDate = new Date(issue.createdAt);
        const startDate = new Date(filter.dateRange.start);
        const endDate = new Date(filter.dateRange.end);
        if (createdDate < startDate || createdDate > endDate) return false;
      }
      
      if (filter.searchQuery) {
        const query = filter.searchQuery.toLowerCase();
        const searchableText = `${issue.title} ${issue.description} ${issue.comments.map(c => c.content).join(' ')}`.toLowerCase();
        if (!searchableText.includes(query)) return false;
      }
      
      return true;
    });
  }

  async getIssueStats(filter?: IssueFilter): Promise<IssueStats> {
    const issues = filter ? await this.searchIssues(filter) : await this.getAllIssues();
    
    const stats: IssueStats = {
      total: issues.length,
      byType: {
        bug: 0,
        feature: 0,
        enhancement: 0,
        documentation: 0,
        question: 0
      },
      byStatus: {
        open: 0,
        'in-progress': 0,
        resolved: 0,
        closed: 0,
        'wont-fix': 0
      },
      byPriority: {
        critical: 0,
        high: 0,
        medium: 0,
        low: 0
      },
      averageResolutionTime: 0,
      trends: {
        period: 'weekly',
        opened: [],
        closed: []
      }
    };

    let totalResolutionTime = 0;
    let resolvedCount = 0;
    let oldestOpen: Issue | undefined;

    for (const issue of issues) {
      stats.byType[issue.type]++;
      stats.byStatus[issue.status]++;
      stats.byPriority[issue.priority]++;

      if (issue.closedAt) {
        const resolutionTime = new Date(issue.closedAt).getTime() - new Date(issue.createdAt).getTime();
        totalResolutionTime += resolutionTime;
        resolvedCount++;
      }

      if (issue.status === 'open' && (!oldestOpen || issue.createdAt < oldestOpen.createdAt)) {
        oldestOpen = issue;
      }
    }

    stats.averageResolutionTime = resolvedCount > 0 ? totalResolutionTime / resolvedCount : 0;
    stats.oldestOpenIssue = oldestOpen;

    const moduleCounts: Record<string, number> = {};
    for (const issue of issues) {
      for (const module of issue.affectedModules) {
        moduleCounts[module] = (moduleCounts[module] || 0) + 1;
      }
    }
    
    const sortedModules = Object.entries(moduleCounts).sort((a, b) => b[1] - a[1]);
    if (sortedModules.length > 0) {
      stats.mostActiveModule = sortedModules[0][0];
    }

    return stats;
  }

  async createMilestone(data: {
    title: string;
    description: string;
    dueDate: string;
    issues?: string[];
  }): Promise<Milestone> {
    const milestone: Milestone = {
      id: this.generateId(),
      title: data.title,
      description: data.description,
      dueDate: data.dueDate,
      status: new Date(data.dueDate) > new Date() ? 'active' : 'overdue',
      issues: data.issues || [],
      progress: {
        total: 0,
        completed: 0,
        percentage: 0
      }
    };

    await this.updateMilestoneProgress(milestone);
    await this.saveMilestone(milestone);
    
    return milestone;
  }

  async updateMilestoneProgress(milestone: Milestone): Promise<void> {
    const issues = await Promise.all(
      milestone.issues.map(id => this.getIssue(id).catch(() => null))
    );

    const validIssues = issues.filter(Boolean) as Issue[];
    const completedIssues = validIssues.filter(i => 
      i.status === 'resolved' || i.status === 'closed'
    );

    milestone.progress = {
      total: validIssues.length,
      completed: completedIssues.length,
      percentage: validIssues.length > 0 
        ? Math.round((completedIssues.length / validIssues.length) * 100)
        : 0
    };
  }

  private async createDefaultTemplates(): Promise<void> {
    const bugTemplate: IssueTemplate = {
      type: 'bug',
      name: 'Bug Report',
      description: 'Report a bug or unexpected behavior',
      sections: [
        {
          title: 'Description',
          prompt: 'A clear description of the bug',
          required: true,
          type: 'multiline'
        },
        {
          title: 'Steps to Reproduce',
          prompt: 'Step-by-step instructions to reproduce the issue',
          required: true,
          type: 'multiline'
        },
        {
          title: 'Expected Behavior',
          prompt: 'What should happen',
          required: true,
          type: 'text'
        },
        {
          title: 'Actual Behavior',
          prompt: 'What actually happens',
          required: true,
          type: 'text'
        },
        {
          title: 'Environment',
          prompt: 'OS, Node version, etc.',
          required: false,
          type: 'multiline'
        }
      ],
      defaultLabels: ['bug'],
      defaultPriority: 'medium'
    };

    const featureTemplate: IssueTemplate = {
      type: 'feature',
      name: 'Feature Request',
      description: 'Suggest a new feature',
      sections: [
        {
          title: 'Feature Description',
          prompt: 'Describe the feature you would like',
          required: true,
          type: 'multiline'
        },
        {
          title: 'Use Case',
          prompt: 'Why is this feature needed?',
          required: true,
          type: 'multiline'
        },
        {
          title: 'Proposed Solution',
          prompt: 'How might this work?',
          required: false,
          type: 'multiline'
        },
        {
          title: 'Alternatives',
          prompt: 'Have you considered any alternatives?',
          required: false,
          type: 'multiline'
        }
      ],
      defaultLabels: ['enhancement'],
      defaultPriority: 'medium'
    };

    await this.saveTemplate(bugTemplate);
    await this.saveTemplate(featureTemplate);
  }

  private async getIssue(issueId: string): Promise<Issue> {
    const issuePath = path.join(this.issuesPath, `${issueId}.json`);
    const content = await fs.readFile(issuePath, 'utf-8');
    return JSON.parse(content);
  }

  private async getAllIssues(): Promise<Issue[]> {
    try {
      const files = await fs.readdir(this.issuesPath);
      const issues = await Promise.all(
        files
          .filter(f => f.endsWith('.json'))
          .map(async f => {
            const content = await fs.readFile(path.join(this.issuesPath, f), 'utf-8');
            return JSON.parse(content) as Issue;
          })
      );
      return issues;
    } catch {
      return [];
    }
  }

  private async saveIssue(issue: Issue): Promise<void> {
    const issuePath = path.join(this.issuesPath, `${issue.id}.json`);
    await fs.writeFile(issuePath, JSON.stringify(issue, null, 2));
  }

  private async saveMilestone(milestone: Milestone): Promise<void> {
    const milestonePath = path.join(this.milestonesPath, `${milestone.id}.json`);
    await fs.writeFile(milestonePath, JSON.stringify(milestone, null, 2));
  }

  private async saveTemplate(template: IssueTemplate): Promise<void> {
    const templatePath = path.join(this.templatesPath, `${template.type}.json`);
    await fs.writeFile(templatePath, JSON.stringify(template, null, 2));
  }

  private generateId(): string {
    return `issue_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }
}