import {
  Injectable,
  Logger,
  NotFoundException,
  BadRequestException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, FindOptionsWhere, In } from 'typeorm';
import {
  Feedback,
  FeedbackType,
  FeedbackStatus,
  FeedbackPriority,
} from './feedback.entity';
import { CreateFeedbackDto } from './dto/create-feedback.dto';
import {
  AnalyticsService,
  AnalyticsEvent,
} from '../../common/services/analytics.service';

export interface FeedbackFilters {
  type?: FeedbackType;
  status?: FeedbackStatus;
  priority?: FeedbackPriority;
  category?: string;
  platform?: string;
  startDate?: Date;
  endDate?: Date;
}

export interface FeedbackStats {
  total: number;
  byType: Record<FeedbackType, number>;
  byStatus: Record<FeedbackStatus, number>;
  byPriority: Record<FeedbackPriority, number>;
  averageResolutionTime: number; // in hours
  responseRate: number; // percentage
}

/**
 * Feedback Service
 *
 * Features:
 * - User feedback collection (bug reports, feature requests, general feedback)
 * - Feedback categorization and sentiment analysis
 * - Priority management and triage
 * - Internal team workflow (assignment, resolution)
 * - Analytics integration for feedback trends
 * - Email notifications for follow-ups
 * - Public feedback upvoting (feature requests)
 *
 * Usage:
 * ```typescript
 * await this.feedbackService.createFeedback(userId, {
 *   type: FeedbackType.BUG_REPORT,
 *   message: 'App crashes when...',
 *   category: 'tracking',
 *   page: '/track/feeding'
 * });
 * ```
 */
@Injectable()
export class FeedbackService {
  private readonly logger = new Logger('FeedbackService');

  constructor(
    @InjectRepository(Feedback)
    private feedbackRepository: Repository<Feedback>,
    private analyticsService: AnalyticsService,
  ) {}

  /**
   * Create new feedback
   */
  async createFeedback(
    userId: string,
    dto: CreateFeedbackDto,
  ): Promise<Feedback> {
    try {
      // Auto-detect sentiment if not provided
      const sentiment = dto.sentiment || this.detectSentiment(dto.message);

      // Auto-assign priority based on type and sentiment
      const priority = this.calculatePriority(dto.type, sentiment);

      const feedback = this.feedbackRepository.create({
        userId,
        type: dto.type,
        message: dto.message,
        title: dto.title,
        category: dto.category,
        page: dto.page,
        platform: dto.platform || 'web',
        appVersion: dto.appVersion,
        browserInfo: dto.browserInfo,
        deviceInfo: dto.deviceInfo,
        metadata: dto.metadata,
        tags: dto.tags,
        userEmail: dto.userEmail,
        contactAllowed: dto.contactAllowed || false,
        sentiment: sentiment as any,
        priority,
        status: FeedbackStatus.NEW,
      });

      const saved = await this.feedbackRepository.save(feedback);

      // Track feedback submission
      await this.analyticsService.trackEvent({
        event: AnalyticsEvent.FEEDBACK_SUBMITTED,
        userId,
        timestamp: new Date(),
        properties: {
          feedbackId: saved.id,
          type: dto.type,
          category: dto.category,
          platform: dto.platform,
          sentiment,
          priority,
        },
      });

      this.logger.log(
        `Feedback created: ${saved.id} (type: ${dto.type}, priority: ${priority})`,
      );

      return saved;
    } catch (error) {
      this.logger.error(`Failed to create feedback: ${error.message}`);
      throw new BadRequestException('Failed to submit feedback');
    }
  }

  /**
   * Get feedback by ID
   */
  async getFeedback(id: string): Promise<Feedback> {
    const feedback = await this.feedbackRepository.findOne({ where: { id } });

    if (!feedback) {
      throw new NotFoundException('Feedback not found');
    }

    return feedback;
  }

  /**
   * Get user's feedback history
   */
  async getUserFeedback(
    userId: string,
    filters?: FeedbackFilters,
  ): Promise<Feedback[]> {
    const where: FindOptionsWhere<Feedback> = { userId };

    if (filters?.type) {
      where.type = filters.type;
    }

    if (filters?.status) {
      where.status = filters.status;
    }

    if (filters?.category) {
      where.category = filters.category;
    }

    return this.feedbackRepository.find({
      where,
      order: { createdAt: 'DESC' },
    });
  }

  /**
   * Get all feedback with filters (admin only)
   */
  async getAllFeedback(
    filters?: FeedbackFilters,
    page: number = 1,
    limit: number = 50,
  ): Promise<{ feedback: Feedback[]; total: number }> {
    const where: FindOptionsWhere<Feedback> = {};

    if (filters?.type) {
      where.type = filters.type;
    }

    if (filters?.status) {
      where.status = filters.status;
    }

    if (filters?.priority) {
      where.priority = filters.priority;
    }

    if (filters?.category) {
      where.category = filters.category;
    }

    if (filters?.platform) {
      where.platform = filters.platform;
    }

    const [feedback, total] = await this.feedbackRepository.findAndCount({
      where,
      order: { priority: 'DESC', createdAt: 'DESC' },
      skip: (page - 1) * limit,
      take: limit,
    });

    return { feedback, total };
  }

  /**
   * Update feedback status (admin only)
   */
  async updateStatus(
    id: string,
    status: FeedbackStatus,
    adminId: string,
    resolution?: string,
  ): Promise<Feedback> {
    const feedback = await this.getFeedback(id);

    feedback.status = status;

    if (
      status === FeedbackStatus.RESOLVED ||
      status === FeedbackStatus.CLOSED
    ) {
      feedback.resolvedAt = new Date();
      feedback.resolvedBy = adminId;
      feedback.resolution = resolution;
    }

    return this.feedbackRepository.save(feedback);
  }

  /**
   * Assign feedback to team member (admin only)
   */
  async assignFeedback(id: string, assignedTo: string): Promise<Feedback> {
    const feedback = await this.getFeedback(id);
    feedback.assignedTo = assignedTo;
    feedback.status = FeedbackStatus.TRIAGED;
    return this.feedbackRepository.save(feedback);
  }

  /**
   * Add internal notes (admin only)
   */
  async addInternalNotes(id: string, notes: string): Promise<Feedback> {
    const feedback = await this.getFeedback(id);
    feedback.internalNotes = notes;
    return this.feedbackRepository.save(feedback);
  }

  /**
   * Upvote feature request
   */
  async upvoteFeedback(id: string, userId: string): Promise<Feedback> {
    const feedback = await this.getFeedback(id);

    if (feedback.type !== FeedbackType.FEATURE_REQUEST) {
      throw new BadRequestException('Can only upvote feature requests');
    }

    // TODO: Track who upvoted to prevent duplicates
    feedback.upvotes += 1;

    await this.analyticsService.trackEvent({
      event: AnalyticsEvent.FEATURE_UPVOTED,
      userId,
      timestamp: new Date(),
      properties: {
        feedbackId: id,
        upvotes: feedback.upvotes,
      },
    });

    return this.feedbackRepository.save(feedback);
  }

  /**
   * Get feedback statistics
   */
  async getFeedbackStats(filters?: FeedbackFilters): Promise<FeedbackStats> {
    const where: FindOptionsWhere<Feedback> = {};

    if (filters?.startDate || filters?.endDate) {
      // TODO: Add date range filtering
    }

    const allFeedback = await this.feedbackRepository.find({ where });

    // Count by type
    const byType = Object.values(FeedbackType).reduce(
      (acc, type) => {
        acc[type] = allFeedback.filter((f) => f.type === type).length;
        return acc;
      },
      {} as Record<FeedbackType, number>,
    );

    // Count by status
    const byStatus = Object.values(FeedbackStatus).reduce(
      (acc, status) => {
        acc[status] = allFeedback.filter((f) => f.status === status).length;
        return acc;
      },
      {} as Record<FeedbackStatus, number>,
    );

    // Count by priority
    const byPriority = Object.values(FeedbackPriority).reduce(
      (acc, priority) => {
        acc[priority] = allFeedback.filter(
          (f) => f.priority === priority,
        ).length;
        return acc;
      },
      {} as Record<FeedbackPriority, number>,
    );

    // Calculate average resolution time
    const resolvedFeedback = allFeedback.filter((f) => f.resolvedAt);
    const totalResolutionTime = resolvedFeedback.reduce((sum, f) => {
      const diff = f.resolvedAt.getTime() - f.createdAt.getTime();
      return sum + diff / (1000 * 60 * 60); // Convert to hours
    }, 0);
    const averageResolutionTime =
      resolvedFeedback.length > 0
        ? totalResolutionTime / resolvedFeedback.length
        : 0;

    // Calculate response rate
    const respondedFeedback = allFeedback.filter(
      (f) => f.status !== FeedbackStatus.NEW,
    );
    const responseRate =
      allFeedback.length > 0
        ? (respondedFeedback.length / allFeedback.length) * 100
        : 0;

    return {
      total: allFeedback.length,
      byType,
      byStatus,
      byPriority,
      averageResolutionTime,
      responseRate,
    };
  }

  /**
   * Get trending feature requests (most upvoted)
   */
  async getTrendingFeatureRequests(limit: number = 10): Promise<Feedback[]> {
    return this.feedbackRepository.find({
      where: {
        type: FeedbackType.FEATURE_REQUEST,
        status: In([FeedbackStatus.NEW, FeedbackStatus.TRIAGED]),
      },
      order: { upvotes: 'DESC', createdAt: 'DESC' },
      take: limit,
    });
  }

  /**
   * Detect sentiment from feedback message
   */
  private detectSentiment(message: string): string {
    const negativePhrases = [
      'bug',
      'crash',
      'broken',
      'error',
      'issue',
      'problem',
      'terrible',
      'awful',
      'worst',
      'hate',
      'frustrated',
      'annoying',
    ];

    const positivePhrases = [
      'love',
      'great',
      'excellent',
      'amazing',
      'awesome',
      'perfect',
      'fantastic',
      'wonderful',
      'appreciate',
      'thank',
    ];

    const lowerMessage = message.toLowerCase();

    const negativeCount = negativePhrases.filter((phrase) =>
      lowerMessage.includes(phrase),
    ).length;
    const positiveCount = positivePhrases.filter((phrase) =>
      lowerMessage.includes(phrase),
    ).length;

    if (negativeCount > positiveCount) {
      return negativeCount >= 3 ? 'very_negative' : 'negative';
    } else if (positiveCount > negativeCount) {
      return positiveCount >= 3 ? 'very_positive' : 'positive';
    }

    return 'neutral';
  }

  /**
   * Calculate priority based on type and sentiment
   */
  private calculatePriority(
    type: FeedbackType,
    sentiment: string,
  ): FeedbackPriority {
    // Critical bugs always high priority
    if (
      type === FeedbackType.BUG_REPORT &&
      (sentiment === 'very_negative' || sentiment === 'negative')
    ) {
      return FeedbackPriority.HIGH;
    }

    // Performance issues are high priority
    if (type === FeedbackType.PERFORMANCE_ISSUE) {
      return FeedbackPriority.HIGH;
    }

    // Feature requests based on sentiment
    if (type === FeedbackType.FEATURE_REQUEST) {
      return sentiment === 'very_positive'
        ? FeedbackPriority.MEDIUM
        : FeedbackPriority.LOW;
    }

    return FeedbackPriority.MEDIUM;
  }
}
