package auctiora;

import io.quarkus.scheduler.Scheduled;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.logging.Logger;

import java.util.List;

/**
 * Quarkus-based Workflow Scheduler using @Scheduled annotations.
 * Replaces the manual ScheduledExecutorService with Quarkus Scheduler.
 *
 * This class coordinates all scheduled workflows using Quarkus's built-in
 * scheduling capabilities with cron expressions.
 */
@ApplicationScoped
public class QuarkusWorkflowScheduler {
   
   private static final Logger LOG = Logger.getLogger(QuarkusWorkflowScheduler.class);
   
   @Inject
   DatabaseService db;
   
   @Inject
   NotificationService notifier;
   
   @Inject
   ObjectDetectionService detector;
   
   @Inject
   ImageProcessingService imageProcessor;
   
   @ConfigProperty(name = "auction.database.path")
   String databasePath;
   
   /**
    * Workflow 1: Import Scraper Data
    * Cron: Every 30 minutes (0 -/30 - - - ?)
    * Purpose: Import new auctions and lots from external scraper
    */
   @Scheduled(cron = "{auction.workflow.scraper-import.cron}", identity = "scraper-import")
   void importScraperData() {
      try {
         LOG.info("📥 [WORKFLOW 1] Importing scraper data...");
         var start = System.currentTimeMillis();
         
         // Import auctions
         var auctions = db.importAuctionsFromScraper();
         LOG.infof("  → Imported %d auctions", auctions.size());
         
         // Import lots
         var lots = db.importLotsFromScraper();
         LOG.infof("  → Imported %d lots", lots.size());
         
         // Import image URLs
         var images = db.getUnprocessedImagesFromScraper();
         LOG.infof("  → Found %d unprocessed images", images.size());
         
         var duration = System.currentTimeMillis() - start;
         LOG.infof("  ✓ Scraper import completed in %dms", duration);
         
         // Trigger notification if significant data imported
         if (auctions.size() > 0 || lots.size() > 10) {
            notifier.sendNotification(
                    String.format("Imported %d auctions, %d lots", auctions.size(), lots.size()),
                    "Data Import Complete",
                    0
                                     );
         }
         
      } catch (Exception e) {
         LOG.errorf(e, "  ❌ Scraper import failed: %s", e.getMessage());
      }
   }
   
   /**
    * Workflow 2: Process Pending Images
    * Cron: Every 1 hour (0 0 * * * ?)
    * Purpose: Download images and run object detection
    */
   @Scheduled(cron = "{auction.workflow.image-processing.cron}", identity = "image-processing")
   void processImages() {
      try {
         LOG.info("🖼️  [WORKFLOW 2] Processing pending images...");
         var start = System.currentTimeMillis();
         
         // Get unprocessed images
         var unprocessedImages = db.getUnprocessedImagesFromScraper();
         
         if (unprocessedImages.isEmpty()) {
            LOG.info("  → No pending images to process");
            return;
         }
         
         LOG.infof("  → Processing %d images", unprocessedImages.size());
         
         var processed = 0;
         var detected  = 0;
         
         for (var imageRecord : unprocessedImages) {
            try {
               // Download image
               var filePath = imageProcessor.downloadImage(
                       imageRecord.url(),
                       imageRecord.saleId(),
                       imageRecord.lotId()
                                                          );
               
               if (filePath != null) {
                  // Run object detection
                  var labels = detector.detectObjects(filePath);
                  
                  // Save to database
                  db.insertImage(imageRecord.lotId(), imageRecord.url(),
                                 filePath, labels);
                  
                  processed++;
                  if (!labels.isEmpty()) {
                     detected++;
                     
                     // Send notification for interesting detections
                     if (labels.size() >= 3) {
                        notifier.sendNotification(
                                String.format("Lot %d: Detected %s",
                                              imageRecord.lotId(),
                                              String.join(", ", labels)),
                                "Objects Detected",
                                0
                                                 );
                     }
                  }
               }
               
               // Rate limiting
               Thread.sleep(500);
               
            } catch (Exception e) {
               LOG.warnf("    ⚠️  Failed to process image: %s", e.getMessage());
            }
         }
         
         var duration = System.currentTimeMillis() - start;
         LOG.infof("  ✓ Processed %d images, detected objects in %d (%.1fs)",
                   processed, detected, duration / 1000.0);
         
      } catch (Exception e) {
         LOG.errorf(e, "  ❌ Image processing failed: %s", e.getMessage());
      }
   }
   
   /**
    * Workflow 3: Monitor Bids
    * Cron: Every 15 minutes (0 -/15 * * * ?)
    * Purpose: Check for bid changes and send notifications
    */
   @Scheduled(cron = "{auction.workflow.bid-monitoring.cron}", identity = "bid-monitoring")
   void monitorBids() {
      try {
         LOG.info("💰 [WORKFLOW 3] Monitoring bids...");
         var start = System.currentTimeMillis();
         
         var activeLots = db.getActiveLots();
         LOG.infof("  → Checking %d active lots", activeLots.size());
         
         // Note: In production, this would call Troostwijk API
         // For now, we just track what's in the database
         // The external scraper updates bids, we just notify
         
         var duration = System.currentTimeMillis() - start;
         LOG.infof("  ✓ Bid monitoring completed in %dms", duration);
         
      } catch (Exception e) {
         LOG.errorf(e, "  ❌ Bid monitoring failed: %s", e.getMessage());
      }
   }
   
   /**
    * Workflow 4: Check Closing Times
    * Cron: Every 5 minutes (0 -/5 * * * ?)
    * Purpose: Send alerts for lots closing soon
    */
   @Scheduled(cron = "{auction.workflow.closing-alerts.cron}", identity = "closing-alerts")
   void checkClosingTimes() {
      try {
         LOG.info("⏰ [WORKFLOW 4] Checking closing times...");
         var start = System.currentTimeMillis();
         
         var activeLots = db.getActiveLots();
         var alertsSent = 0;
         
         for (var lot : activeLots) {
            if (lot.closingTime() == null) continue;
            
            var minutesLeft = lot.minutesUntilClose();
            
            // Alert for lots closing in 5 minutes
            if (minutesLeft <= 5 && minutesLeft > 0 && !lot.closingNotified()) {
               var message = String.format("Kavel %d sluit binnen %d min.",
                                           lot.lotId(), minutesLeft);
               
               notifier.sendNotification(message, "Lot Closing Soon", 1);
               
               // Mark as notified
               var updated = new Lot(
                       lot.saleId(), lot.lotId(), lot.title(), lot.description(),
                       lot.manufacturer(), lot.type(), lot.year(), lot.category(),
                       lot.currentBid(), lot.currency(), lot.url(),
                       lot.closingTime(), true
               );
               db.updateLotNotificationFlags(updated);
               
               alertsSent++;
            }
         }
         
         var duration = System.currentTimeMillis() - start;
         LOG.infof("  → Sent %d closing alerts in %dms", alertsSent, duration);
         
      } catch (Exception e) {
         LOG.errorf(e, "  ❌ Closing alerts failed: %s", e.getMessage());
      }
   }
   
   /**
    * Event-driven trigger: New auction discovered
    */
   public void onNewAuctionDiscovered(AuctionInfo auction) {
      LOG.infof("📣 EVENT: New auction discovered - %s", auction.title());
      
      try {
         db.upsertAuction(auction);
         
         notifier.sendNotification(
                 String.format("New auction: %s\nLocation: %s\nLots: %d",
                               auction.title(), auction.location(), auction.lotCount()),
                 "New Auction Discovered",
                 0
                                  );
         
      } catch (Exception e) {
         LOG.errorf(e, "  ❌ Failed to handle new auction: %s", e.getMessage());
      }
   }
   
   /**
    * Event-driven trigger: Bid change detected
    */
   public void onBidChange(Lot lot, double previousBid, double newBid) {
      LOG.infof("📣 EVENT: Bid change on lot %d (€%.2f → €%.2f)",
                lot.lotId(), previousBid, newBid);
      
      try {
         db.updateLotCurrentBid(lot);
         
         notifier.sendNotification(
                 String.format("Nieuw bod op kavel %d: €%.2f (was €%.2f)",
                               lot.lotId(), newBid, previousBid),
                 "Kavel Bieding Update",
                 0
                                  );
         
      } catch (Exception e) {
         LOG.errorf(e, "  ❌ Failed to handle bid change: %s", e.getMessage());
      }
   }
   
   /**
    * Event-driven trigger: Objects detected in image
    */
   public void onObjectsDetected(int lotId, List<String> labels) {
      LOG.infof("📣 EVENT: Objects detected in lot %d - %s",
                lotId, String.join(", ", labels));
      
      try {
         if (labels.size() >= 2) {
            notifier.sendNotification(
                    String.format("Lot %d contains: %s", lotId, String.join(", ", labels)),
                    "Objects Detected",
                    0
                                     );
         }
      } catch (Exception e) {
         LOG.errorf(e, "  ❌ Failed to send detection notification: %s", e.getMessage());
      }
   }
}
