// cronJobs.js
import cron from 'node-cron';
import Award from '../models/award.js';
import Vote from '../models/vote.js';
import SiteOfTheDayState from '../models/siteOfTheDayState.js';
import WebsiteSubmission from '../models/websiteSubmission.js';
import SubmitDirectory from '../models/submitDirectory.js';
import SubmitAiWebsite from '../models/submitAiWebsite.js';
import PostJob from '../models/postJob.js';

// Reusable award calculation function (same as your POST route)
export const calculateAwards = async () => {
  try {
    const now = new Date();
    const results = {};

    console.log('🕒 CRON: Starting automatic award calculation at', now);

    // Helper function to calculate award for a time period
    const calculateAward = async (awardType, startDate, endDate) => {
      // Find the highest score in the time period
      const highestScoreVote = await Vote.findOne({
        createdAt: { $gte: startDate, $lte: endDate },
      }).sort({ score: -1 });

      if (!highestScoreVote) {
        return { awardType, message: "No votes found in this period" };
      }

      // Find all websites that received votes with this highest score in the period
      const winningVotes = await Vote.find({
        createdAt: { $gte: startDate, $lte: endDate },
        score: highestScoreVote.score,
      }).distinct("websiteId");

      // Check if award already exists for this period
      const existingAwards = await Award.find({
        awardType,
        createdAt: { $gte: startDate },
      });

      const existingWebsiteIds = existingAwards.map((award) =>
        award.websiteId.toString()
      );

      // Create awards for new winners
      const newAwards = [];
      for (const websiteId of winningVotes) {
        if (!existingWebsiteIds.includes(websiteId.toString())) {
          const newAward = new Award({
            websiteId,
            awardType,
          });
          await newAward.save();
          newAwards.push(newAward);
        }
      }

      return {
        awardType,
        highestScore: highestScoreVote.score,
        winners: winningVotes.length,
        newAwards: newAwards.length,
      };
    };

    // Calculate SOTD (Site of the Day)
    const todayStart = new Date(now);
    todayStart.setHours(0, 0, 0, 0);
    const todayEnd = new Date(now);
    todayEnd.setHours(23, 59, 59, 999);
    results.SOTD = await calculateAward("SOTD", todayStart, todayEnd);

    // Calculate SOTW (Site of the Week)
    const weekStart = new Date(now);
    weekStart.setDate(now.getDate() - now.getDay()); // Start of week (Sunday)
    weekStart.setHours(0, 0, 0, 0);
    const weekEnd = new Date(weekStart);
    weekEnd.setDate(weekStart.getDate() + 6);
    weekEnd.setHours(23, 59, 59, 999);
    results.SOTW = await calculateAward("SOTW", weekStart, weekEnd);

    // Calculate SOTM (Site of the Month)
    const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
    const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0);
    monthEnd.setHours(23, 59, 59, 999);
    results.SOTM = await calculateAward("SOTM", monthStart, monthEnd);

    // Calculate SOTY (Site of the Year)
    const yearStart = new Date(now.getFullYear(), 0, 1);
    const yearEnd = new Date(now.getFullYear(), 11, 31);
    yearEnd.setHours(23, 59, 59, 999);
    results.SOTY = await calculateAward("SOTY", yearStart, yearEnd);

    console.log('✅ CRON: Award calculation completed successfully');
    return results;

  } catch (err) {
    console.error('❌ CRON: Error calculating awards:', err);
    throw err;
  }
};

/**
 * Enhanced Site of the Day Selection Logic
 * 
 * Primary rule: Select the website with the highest votes from yesterday
 * Tie handling: Show one per day, save others in pending queue
 * No-vote handling: Use pending queue, don't repeat last shown
 * Anti-repeat: Same site can't appear on consecutive days
 * Fallback: If no votes and no pending, select next best eligible site
 */
export const selectSiteOfTheDay = async () => {
  try {
    const now = new Date();
    console.log('🎯 Starting SOTD selection at', now.toISOString());

    // Get or create the SOTD state
    const state = await SiteOfTheDayState.getState();

    // Define "yesterday" - last completed 24-hour period
    const yesterdayEnd = new Date(now);
    yesterdayEnd.setHours(0, 0, 0, 0); // Start of today
    const yesterdayStart = new Date(yesterdayEnd);
    yesterdayStart.setDate(yesterdayStart.getDate() - 1); // Start of yesterday

    console.log('📅 Checking votes from', yesterdayStart.toISOString(), 'to', yesterdayEnd.toISOString());

    // Step 1: Find all votes from yesterday and aggregate by website
    const votesYesterday = await Vote.aggregate([
      {
        $match: {
          createdAt: { $gte: yesterdayStart, $lt: yesterdayEnd }
        }
      },
      {
        $group: {
          _id: "$websiteId",
          totalVotes: { $sum: 1 },
          averageScore: { $avg: "$score" },
          maxScore: { $max: "$score" }
        }
      },
      {
        $sort: { totalVotes: -1, averageScore: -1 } // Sort by vote count, then by average score
      }
    ]);

    console.log(`📊 Found ${votesYesterday.length} websites with votes yesterday`);

    let selectedWebsiteId = null;
    let selectedAwardId = null;
    let selectionReason = 'fallback';
    let pendingCandidates = [...state.pendingSiteOfTheDayCandidates];

    // Get recently shown IDs to prevent repetition
    const recentlyShownIds = state.recentlyShownWebsiteIds
      .filter(item => {
        // Keep items from last 7 days
        const daysDiff = (now - item.shownDate) / (1000 * 60 * 60 * 24);
        return daysDiff <= 7;
      })
      .map(item => item.websiteId.toString());

    // Add current SOTD to recently shown if it exists
    if (state.currentWebsiteId) {
      recentlyShownIds.push(state.currentWebsiteId.toString());
    }

    // RULE 1: Primary - Select from yesterday's votes
    if (votesYesterday.length > 0) {
      const highestVoteCount = votesYesterday[0].totalVotes;

      // Find all websites tied with the highest vote count
      const tiedWinners = votesYesterday.filter(v => v.totalVotes === highestVoteCount);

      console.log(`🏆 Found ${tiedWinners.length} website(s) with highest vote count: ${highestVoteCount}`);

      // Filter out recently shown websites
      const eligibleWinners = tiedWinners.filter(
        w => !recentlyShownIds.includes(w._id.toString())
      );

      if (eligibleWinners.length > 0) {
        // Select the first eligible winner (deterministic)
        const winner = eligibleWinners[0];
        selectedWebsiteId = winner._id;
        selectionReason = 'votes_winner';

        console.log(`✅ Selected winner from votes: ${selectedWebsiteId}`);

        // If there are more tied winners, add them to pending queue
        if (eligibleWinners.length > 1) {
          const newPending = eligibleWinners.slice(1).map(w => ({
            websiteId: w._id,
            score: w.averageScore,
            votesCount: w.totalVotes,
            tiedDate: now
          }));

          pendingCandidates.push(...newPending);
          console.log(`📝 Added ${newPending.length} tied winners to pending queue`);
        }
      } else {
        console.log('⚠️ All vote winners were recently shown, checking pending queue...');
      }
    }

    // RULE 2: No votes yesterday OR all winners recently shown - use pending queue
    if (!selectedWebsiteId && pendingCandidates.length > 0) {
      console.log(`📋 Checking pending queue (${pendingCandidates.length} candidates)...`);

      // Filter out recently shown websites from pending queue
      const eligiblePending = pendingCandidates.filter(
        c => !recentlyShownIds.includes(c.websiteId.toString())
      );

      if (eligiblePending.length > 0) {
        // Select the first pending candidate (FIFO - deterministic)
        const pendingWinner = eligiblePending[0];
        selectedWebsiteId = pendingWinner.websiteId;
        selectionReason = 'pending_queue';

        console.log(`✅ Selected from pending queue: ${selectedWebsiteId}`);

        // Remove the selected candidate from pending queue
        pendingCandidates = pendingCandidates.filter(
          c => c.websiteId.toString() !== selectedWebsiteId.toString()
        );
      } else {
        console.log('⚠️ All pending candidates were recently shown');
      }
    }

    // RULE 3: Fallback - no votes and no eligible pending candidates
    if (!selectedWebsiteId) {
      console.log('🔄 Using fallback selection...');

      // Find websites with awards that haven't been shown recently
      const fallbackWebsite = await Award.findOne({
        awardType: "SOTD",
        websiteId: { $nin: recentlyShownIds }
      })
        .sort({ createdAt: -1 })
        .populate('websiteId');

      if (fallbackWebsite) {
        selectedWebsiteId = fallbackWebsite.websiteId._id;
        selectedAwardId = fallbackWebsite._id;
        selectionReason = 'fallback';
        console.log(`✅ Fallback selection: ${selectedWebsiteId}`);
      } else {
        // Last resort: pick any SOTD award (even if shown recently)
        const lastResort = await Award.findOne({ awardType: "SOTD" })
          .sort({ createdAt: -1 });

        if (lastResort) {
          selectedWebsiteId = lastResort.websiteId;
          selectedAwardId = lastResort._id;
          selectionReason = 'fallback';
          console.log(`⚠️ Last resort fallback: ${selectedWebsiteId}`);
        } else {
          console.log('❌ No SOTD awards found in database');
          return null;
        }
      }
    }

    // Create or find the award for the selected website if needed
    if (!selectedAwardId) {
      // Check if an award already exists for this website
      let existingAward = await Award.findOne({
        websiteId: selectedWebsiteId,
        awardType: "SOTD"
      });

      if (!existingAward) {
        // Create new award
        existingAward = await Award.create({
          websiteId: selectedWebsiteId,
          awardType: "SOTD"
        });
        console.log(`🆕 Created new SOTD award for website: ${selectedWebsiteId}`);
      }

      selectedAwardId = existingAward._id;
    }

    // Update the SOTD state
    state.currentSiteOfTheDay = selectedAwardId;
    state.currentWebsiteId = selectedWebsiteId;
    state.lastSelectionDate = now;
    state.lastUpdateReason = selectionReason;
    state.pendingSiteOfTheDayCandidates = pendingCandidates;

    // Update recently shown list (keep last 7 days)
    state.recentlyShownWebsiteIds = [
      { websiteId: selectedWebsiteId, shownDate: now },
      ...state.recentlyShownWebsiteIds
        .filter(item => {
          const daysDiff = (now - item.shownDate) / (1000 * 60 * 60 * 24);
          return daysDiff <= 7 && item.websiteId.toString() !== selectedWebsiteId.toString();
        })
    ].slice(0, 20); // Keep max 20 recent entries

    // Update statistics
    if (selectionReason === 'votes_winner') {
      state.statistics.totalSelectionsFromVotes += 1;
    } else if (selectionReason === 'pending_queue') {
      state.statistics.totalSelectionsFromPending += 1;
    } else if (selectionReason === 'fallback') {
      state.statistics.totalFallbackSelections += 1;
    }
    state.statistics.lastCronRunDate = now;

    await state.save();

    console.log('✅ SOTD state updated successfully');
    console.log(`   Selected Website: ${selectedWebsiteId}`);
    console.log(`   Selection Reason: ${selectionReason}`);
    console.log(`   Pending Queue Size: ${pendingCandidates.length}`);
    console.log(`   Recently Shown Count: ${state.recentlyShownWebsiteIds.length}`);

    return {
      selectedWebsiteId,
      selectedAwardId,
      selectionReason,
      pendingCount: pendingCandidates.length,
      votesYesterdayCount: votesYesterday.length
    };

  } catch (err) {
    console.error('❌ Error in selectSiteOfTheDay:', err);
    throw err;
  }
};

/**
 * Cleanup Pending Submissions
 * 
 * Deletes submissions that have been in 'pending_payment' status for more than 1 hour
 * Runs every hour to keep the database clean
 */
export const cleanupPendingSubmissions = async () => {
  try {
    const now = new Date();
    const oneHourAgo = new Date(now.getTime() - 20 * 60 * 1000); // 1 hour ago

    console.log('🧹 Starting pending submissions cleanup at', now.toISOString());

    // Delete pending website submissions older than 1 hour
    const deletedWebsites = await WebsiteSubmission.deleteMany({
      status: 'pending_payment',
      createdAt: { $lt: oneHourAgo }
    });

    // Delete pending directory submissions older than 1 hour
    const deletedDirectories = await SubmitDirectory.deleteMany({
      status: 'pending_payment',
      createdAt: { $lt: oneHourAgo }
    });

    // Delete pending AI website submissions older than 1 hour
    const deletedAIWebsites = await SubmitAiWebsite.deleteMany({
      status: 'pending_payment',
      createdAt: { $lt: oneHourAgo }
    });

    // Delete pending job postings older than 1 hour
    const deletedJobs = await PostJob.deleteMany({
      status: 'pending_payment',
      createdAt: { $lt: oneHourAgo }
    });

    const totalDeleted =
      deletedWebsites.deletedCount +
      deletedDirectories.deletedCount +
      deletedAIWebsites.deletedCount +
      deletedJobs.deletedCount;

    console.log('✅ Cleanup completed:');
    console.log(`   - Websites: ${deletedWebsites.deletedCount}`);
    console.log(`   - Directories: ${deletedDirectories.deletedCount}`);
    console.log(`   - AI Websites: ${deletedAIWebsites.deletedCount}`);
    console.log(`   - Jobs: ${deletedJobs.deletedCount}`);
    console.log(`   - Total: ${totalDeleted}`);

    return {
      websites: deletedWebsites.deletedCount,
      directories: deletedDirectories.deletedCount,
      aiWebsites: deletedAIWebsites.deletedCount,
      jobs: deletedJobs.deletedCount,
      total: totalDeleted
    };

  } catch (err) {
    console.error('❌ Error in cleanupPendingSubmissions:', err);
    throw err;
  }
};

// Setup cron job to run daily at midnight
export const setupCronJobs = () => {

  // Run SOTD selection daily at midnight
  cron.schedule('0 0 * * *', async () => {
    console.log('⏰ CRON: Running SOTD selection (Daily at midnight)...');
    try {
      await selectSiteOfTheDay();
      console.log('✅ CRON: SOTD selection completed');
    } catch (error) {
      console.error('❌ CRON: SOTD selection failed:', error);
    }
  }, {
    timezone: "Asia/Dubai" // Set your timezone
  });

  // Run other awards calculation at midnight
  cron.schedule('55 23 * * *', async () => {
    console.log('⏰ CRON: Midnight - Calculating weekly/monthly/yearly awards...');
    try {
      await calculateAwards();
      console.log('✅ CRON: Awards calculation completed');
    } catch (error) {
      console.error('❌ CRON: Awards calculation failed:', error);
    }
  }, {
    timezone: "Asia/Dubai" // Set your timezone
  });

  // ✅ NEW: Cleanup pending submissions every hour
  cron.schedule('0 * * * *', async () => {
    console.log('⏰ CRON: Running pending submissions cleanup...');
    try {
      await cleanupPendingSubmissions();
      console.log('✅ CRON: Cleanup completed');
    } catch (error) {
      console.error('❌ CRON: Cleanup failed:', error);
    }
  }, {
    timezone: "Asia/Dubai"
  });

  console.log('📅 Cron jobs scheduled:');
  console.log('   - SOTD Selection: Daily at midnight (00:00)');
  console.log('   - Other Awards: Daily at 23:55');
  console.log('   - Pending Submissions Cleanup: Every hour');
};