import { Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { startOfMonth, endOfMonth } from 'date-fns';

import { NotificationsService } from '../notifications/notifications.service';

@Injectable()
export class PerformanceService implements OnModuleInit {
  constructor(
    private prisma: PrismaService,
    private notifications: NotificationsService
  ) { }

  async onModuleInit() {
    try {
      // Ensure systemconfig table exists (for MySQL)
      await this.prisma.$executeRawUnsafe(`
        CREATE TABLE IF NOT EXISTS systemconfig (
          id VARCHAR(36) PRIMARY KEY,
          \`key\` VARCHAR(100) UNIQUE NOT NULL,
          value TEXT,
          updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
        )
      `);

      // Seed default ratios if not present
      const existing: any = await this.prisma.$queryRaw`SELECT id FROM systemconfig WHERE \`key\` = 'funnel_ratios' LIMIT 1`;
      if (!existing || existing.length === 0) {
        const defaultRatios = JSON.stringify({ callsToQuality: 5, qualityToPotential: 3.33, potentialToDemo: 1.5, demoToWon: 2 });
        const id = require('crypto').randomUUID();
        await this.prisma.$executeRawUnsafe(`
          INSERT INTO systemconfig (id, \`key\`, value, updatedAt) 
          VALUES ('${id}', 'funnel_ratios', '${defaultRatios}', NOW())
        `);
      }
    } catch (e) {
      console.error('Error initializing PerformanceService (systemconfig):', e.message);
    }
  }

  async calculateUserScore(userId: string) {
    const start = startOfMonth(new Date());
    const end = endOfMonth(new Date());

    // 1. Fetch Data
    const [target, opportunities, enquiries, followups, strategicActivities] = await Promise.all([
      this.prisma.target.findFirst({
        where: { userId, month: new Date().getMonth() + 1, year: new Date().getFullYear() },
      }),
      this.prisma.opportunity.findMany({
        where: {
          OR: [
            { assignedTo: userId },
            { creatorId: userId },
            { demoOwnerId: userId },
            { closingOwnerId: userId }
          ],
          updatedAt: { gte: start, lte: end }
        }
      }),
      this.prisma.enquiry.findMany({ where: { userId, createdAt: { gte: start, lte: end } } }),
      this.prisma.followup.findMany({ where: { userId, date: { gte: start, lte: end } } }),
      this.prisma.strategicActivity.findMany({ where: { userId, date: { gte: start, lte: end } } }),
    ]);

    if (!target) return { message: 'No target set for this month' };

    // 2. Revenue Achievement (Weight: 40) - LEAD SHARING LOGIC (50% Creator / 50% Closer)
    const totalRevenue = opportunities.reduce((sum, o) => {
      if (o.stage !== 'SALES') return sum;

      let contribution = 0;
      // 50% goes to the Lead Creator
      if (o.creatorId === userId) contribution += o.value * 0.5;

      // 50% goes to the Closer (closingOwnerId or assignedTo if not specified)
      if (o.closingOwnerId === userId || (!o.closingOwnerId && o.assignedTo === userId)) {
        contribution += o.value * 0.5;
      }

      return sum + contribution;
    }, 0);

    const revenueScore = Math.min(40, (totalRevenue / target.monthlyTarget) * 40);

    // 3. Conversion Efficiency (Weight: 20)
    const wonCount = opportunities.filter(o => o.stage === 'SALES' && (o.closingOwnerId === userId || o.assignedTo === userId)).length;
    const conversionEfficiency = enquiries.length > 0 ? (wonCount / enquiries.length) : 0;
    const conversionScore = Math.min(20, conversionEfficiency * 100 * 0.2); // Scaling 100% to 20 points

    // 4. Activity Discipline (Weight: 15) - Strategic Activities
    const activityCount = strategicActivities.length;
    const activityBenchmark = 20; // Assume 20 activities per month benchmark
    const activityScore = Math.min(15, (activityCount / activityBenchmark) * 15);

    // 5. Follow-up Completion (Weight: 15)
    const completedFollowups = followups.filter(f => f.status === 'DONE').length;
    const followupScore = followups.length > 0 ? (completedFollowups / followups.length) * 15 : 15;

    // 6. Data Quality (Weight: 10) - Mandatory fields in Demo stage
    const opportunitiesWithDemo = opportunities.filter(o => o.isDemoDone);
    const qualityOpps = opportunitiesWithDemo.filter(o => o.demoPersonName && o.demoContactDetails && o.keyQueries);
    const dataQualityScore = opportunitiesWithDemo.length > 0 ? (qualityOpps.length / opportunitiesWithDemo.length) * 10 : 10;

    const totalScore = revenueScore + conversionScore + activityScore + followupScore + dataQualityScore;

    // 7. Live Performance Tag
    let tag = 'ON_TRACK';
    if (totalScore < 50) tag = 'UNDERPERFORM';
    else if (totalScore < 80) tag = 'RISK';

    // 8. Save Score
    const result = await this.prisma.performanceScore.create({
      data: {
        userId,
        score: totalScore,
        revenueScore,
        conversionScore,
        activityScore,
        disciplineScore: followupScore,
        dataQualityScore,
        tag,
      },
    });

    // 9. Send Notification
    await this.notifications.create(
      userId,
      'Performance Update',
      `Your performance score for this month is ${Math.round(totalScore)}/100 (${tag}).`,
      'PERFORMANCE'
    );

    return result;
  }

  async getLatestScore(userId: string) {
    return this.prisma.performanceScore.findFirst({
      where: { userId },
      orderBy: { date: 'desc' },
    });
  }

  async getPerformanceTrend(userId: string) {
    return this.prisma.performanceScore.findMany({
      where: { userId },
      orderBy: { date: 'asc' },
      take: 12, // Last 12 records
    });
  }

  async getFunnelData(userId: string) {
    const start = startOfMonth(new Date());
    const end = endOfMonth(new Date());

    const [activities, opportunities] = await Promise.all([
      this.prisma.strategicActivity.findMany({ where: { userId, date: { gte: start, lte: end } } }),
      this.prisma.opportunity.findMany({ where: { assignedTo: userId, updatedAt: { gte: start, lte: end } } }),
    ]);

    // Actual Counts
    const actualCalls = activities.length;
    const actualQuality = opportunities.filter(o => ['QUALIFIED', 'POTENTIAL', 'SALES'].includes(o.stage) || o.isDemoDone).length;
    const actualPotential = opportunities.filter(o => ['POTENTIAL', 'SALES'].includes(o.stage) || o.isDemoDone).length;
    const actualDemo = opportunities.filter(o => o.isDemoDone || o.stage === 'SALES').length;
    const actualWon = opportunities.filter(o => o.stage === 'SALES').length;

    // Default ratios (configurable)
    const DEFAULT_RATIOS = { callsToQuality: 5, qualityToPotential: 3.33, potentialToDemo: 1.5, demoToWon: 2 };
    // Try loading custom config from metadata table or fallback
    let ratios = DEFAULT_RATIOS;
    try {
      const cfg: any = await this.prisma.$queryRaw`SELECT value FROM systemconfig WHERE key = 'funnel_ratios' LIMIT 1`;
      if (cfg && cfg[0]) ratios = JSON.parse(cfg[0].value);
    } catch { }

    // Ideal Funnel (derived from actual calls and configured ratios)
    const idealQuality = actualCalls / ratios.callsToQuality;
    const idealPotential = idealQuality / ratios.qualityToPotential;
    const idealDemo = idealPotential / ratios.potentialToDemo;
    const idealWon = idealDemo / ratios.demoToWon;

    return {
      actual: { calls: actualCalls, quality: actualQuality, potential: actualPotential, demo: actualDemo, won: actualWon },
      ideal: { calls: actualCalls, quality: Math.round(idealQuality), potential: Math.round(idealPotential), demo: Math.round(idealDemo), won: Math.round(idealWon) },
      deviation: { quality: actualQuality - idealQuality, potential: actualPotential - idealPotential, demo: actualDemo - idealDemo, won: actualWon - idealWon },
      ratios
    };
  }

  async getFunnelConfig() {
    const DEFAULT = { callsToQuality: 5, qualityToPotential: 3.33, potentialToDemo: 1.5, demoToWon: 2 };
    try {
      const cfg: any = await this.prisma.$queryRaw`SELECT value FROM systemconfig WHERE key = 'funnel_ratios' LIMIT 1`;
      return cfg && cfg[0] ? JSON.parse(cfg[0].value) : DEFAULT;
    } catch { return DEFAULT; }
  }

  async setFunnelConfig(ratios: { callsToQuality: number; qualityToPotential: number; potentialToDemo: number; demoToWon: number }) {
    const value = JSON.stringify(ratios);
    try {
      const exists: any = await this.prisma.$queryRaw`SELECT id FROM systemconfig WHERE key = 'funnel_ratios' LIMIT 1`;
      if (exists && exists[0]) {
        await this.prisma.$executeRaw`UPDATE systemconfig SET value = ${value}, updatedAt = NOW() WHERE key = 'funnel_ratios'`;
      } else {
        const id = require('crypto').randomUUID();
        await this.prisma.$executeRaw`INSERT INTO systemconfig (id, key, value, updatedAt) VALUES (${id}, 'funnel_ratios', ${value}, NOW())`;
      }
    } catch { }
    return ratios;
  }

  async getTeamFunnel() {
    const start = startOfMonth(new Date());
    const end = endOfMonth(new Date());
    const users = await this.prisma.user.findMany({
      where: { status: 'APPROVED' },
      select: { id: true, name: true, role: true }
    });
    const results = await Promise.all(users.map(async u => {
      const funnel = await this.getFunnelData(u.id);
      return { ...u, funnel };
    }));
    return results;
  }

  async getQuarterlyStatus(userId: string) {
    const scores = await this.prisma.performanceScore.findMany({
      where: { userId },
      orderBy: { date: 'desc' },
      take: 3,
    });

    if (scores.length < 1) return { status: 'NORMAL', suggestions: [] };

    const lastTwoMonthsBelow = scores.length >= 2 && scores.slice(0, 2).every(s => s.score < 50);
    const lastThreeMonthsBelow = scores.length === 3 && scores.every(s => s.score < 50);

    let status = 'NORMAL';
    if (lastThreeMonthsBelow) status = 'ACTION';
    else if (lastTwoMonthsBelow) status = 'WARNING';

    // Automated Improvement Suggestions
    const latest = scores[0];
    const suggestions: string[] = [];
    if (latest.score < 80) {
      if (latest.revenueScore < 20) suggestions.push('Focus on high-value closures to meet revenue targets.');
      if (latest.conversionScore < 10) suggestions.push('Improve conversion rate by qualifying leads better before the Demo stage.');
      if (latest.activityScore < 7) suggestions.push('Increase daily call and meeting activity to fill your pipeline.');
      if (latest.disciplineScore < 7) suggestions.push('Ensure all follow-ups are marked as complete on time.');
      if (latest.dataQualityScore < 5) suggestions.push('Fill in all mandatory fields during the Demo stage to improve data quality.');
    } else {
      suggestions.push('Keep up the great work! You are exceeding your targets.');
    }

    if (status !== 'NORMAL') {
      await this.notifications.create(
        userId,
        `PERFORMANCE ${status}`,
        `Your performance has been below minimum for ${status === 'ACTION' ? '3' : '2'} months. Please check your improvement plan.`,
        'PERFORMANCE_ALERT'
      );
    }

    return {
      status,
      suggestions,
      recentScores: scores.map(s => ({ date: s.date, score: s.score })),
    };
  }

  async getTeamPerformance() {
    const users = await this.prisma.user.findMany({
      where: { status: 'APPROVED' },
      select: {
        id: true,
        name: true,
        role: true,
        performanceScores: {
          orderBy: { date: 'desc' },
          take: 1,
        },
      },
    });

    return users.map(u => ({
      id: u.id,
      name: u.name,
      role: u.role,
      score: u.performanceScores[0]?.score || 0,
      tag: u.performanceScores[0]?.tag || 'N/A',
      lastUpdated: u.performanceScores[0]?.date || null,
    }));
  }
}
