package puzzle;

import lombok.val;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import puzzle.SwedishGenerator.DictEntry;
import puzzle.SwedishGenerator.Rng;
import puzzle.SwedishGenerator.Slotinfo;
import puzzle.SwedishGenerator.Grid;

import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;
import java.util.stream.IntStream;

import static org.junit.jupiter.api.Assertions.assertTrue;
import static puzzle.SwedishGenerator.fillMask;

public class PerformanceTest {
   
     final DictEntry[] EN = DictData.DICT.index();
   @Test
   void testPerformance() {
      val rng = new Rng(42);
      
      // 1. Stress test Clue Generation (Mask Generation)
      System.out.println("[DEBUG_LOG] --- Mask Generation Performance ---");
      int[] clueSizes = { 20, 25, 30 };
      for (int size : clueSizes) {
         long t0     = System.currentTimeMillis();
         val  masker = new Masker(rng, new int[SwedishGenerator.STACK_SIZE], Masker.Clues.createEmpty());
         // Increased population and generations for stress
         val    mask     = masker.generateMask(size, 200, 100, 50);
         long   t1       = System.currentTimeMillis();
         double duration = (t1 - t0) / 1000.0;
         System.out.printf(Locale.ROOT, "[DEBUG_LOG] Size %d (pop=200, gen=100): %.3fs%n", size, duration);
         // Basic sanity check: should not take forever
         assertTrue(duration < 10.0, "Mask generation took too long for size " + size);
      }
      
      // 2. Stress test Word Filler
      System.out.println("[DEBUG_LOG] \n--- Word Filler Performance ---");
      for (int size : clueSizes) {
         val masker = new Masker(rng, new int[SwedishGenerator.STACK_SIZE], Masker.Clues.createEmpty());
         val mask   = masker.generateMask(size, 100, 50, 20);
         
         val slotInfo = Masker.slots(mask, EN);
         val grid     = mask.toGrid();
         
         long t0 = System.currentTimeMillis();
         // Try to fill multiple times to get a better average
         int  iterations      = 1;
         long totalNodes      = 0;
         long totalBacktracks = 0;
         int  successCount    = 0;
         for (int i = 0; i < iterations; i++) {
            val result = fillMask(rng, slotInfo, grid.copy(), false);
            if (result.ok()) successCount++;
            totalNodes += result.nodes();
            totalBacktracks += result.backtracks();
         }
         long   t1            = System.currentTimeMillis();
         double totalDuration = (t1 - t0) / 1000.0;
         
         System.out.printf(Locale.ROOT, "[DEBUG_LOG] Size %d: %d/%d SUCCESS | avg nodes=%d | avg backtracks=%d | total time=%.3fs%n",
                           size, successCount, iterations, totalNodes / iterations, totalBacktracks / iterations, totalDuration);
      }
   }
    void main() {
      testIncrementalComplexity();
   }
   @Test
   void testIncrementalComplexity() {
      
      // Use the complex mask from Main.java
      String maskStr = "1    0000\n" +
                       "1        \n" +
                       "00 01    \n" +
                       "    1    \n" +
                       "    1    \n" +
                       "  2 1    \n" +
                       "  1      \n" +
                       "221    22\n";
      val mask     = Masker.Clues.parse(maskStr);
      val allSlots = Masker.slots(mask.c(), EN);
//mask.toGrid()
      System.out.println("[DEBUG_LOG] \n--- Incremental Complexity Test ---");
      System.out.println("[DEBUG_LOG] Full Slot Layout:");
      visualizeSlots(allSlots);
      
      for (int i = 10; i <= allSlots.length; i++) {
         val subset = Arrays.copyOf(allSlots, i);
        // Arrays.sort(subset, Comparator.comparingInt(Slotinfo::score));
         System.out.printf("[DEBUG_LOG] Testing with first %d slots%n of %s", i, allSlots.length);
         var grid = Slotinfo.grid(subset);
         visualizeSlots(subset);
         measureFill(new Rng(123 + i), subset, grid, "Subset size " + i);
      }
   }
   
   @Test
   void testSingleSlotResolution() {
      val rng = new Rng(42);
      
      // A single horizontal slot at (0,0)
      val mask  = Masker.Clues.parse("1        \n");
      val slots = Masker.slots(mask.c(), EN);
      
      System.out.println("[DEBUG_LOG] \n--- Single Slot Resolution ---");
      if (slots.length > 0) {
         measureFill(rng, slots, mask.toGrid(), "Single Slot");
      } else {
         System.out.println("[DEBUG_LOG] Error: No slots found in mask.");
      }
   }
   
   private void measureFill(Rng rng, Slotinfo[] slots, Grid grid, String label) {
      long t0              = System.currentTimeMillis();
      int  iterations      = 1;
      long totalNodes      = 0;
      long totalBacktracks = 0;
      int  successCount    = 0;
      
      for (int i = 0; i < iterations; i++) {
         // Reset assignments for each iteration
         for (Slotinfo s : slots) s.assign().w = 0;
         
         val result = fillMask(rng, slots, grid.copy(), false);
         if (result.ok()) successCount++;
         totalNodes += result.nodes();
         totalBacktracks += result.backtracks();
      }
      long   t1            = System.currentTimeMillis();
      double totalDuration = (t1 - t0) / 1000.0;
      
      System.out.printf(Locale.ROOT, "[DEBUG_LOG] %s: %d/%d SUCCESS | avg nodes=%d | avg backtracks=%d | total time=%.3fs%n",
                        label, successCount, iterations, totalNodes / iterations, totalBacktracks / iterations, totalDuration);
   }
   
   private void visualizeSlots(Slotinfo[] slots) {
      int      R       = SwedishGenerator.R;
      int      C       = SwedishGenerator.C;
      char[][] display = new char[R][C];
      for (int r = 0; r < R; r++) Arrays.fill(display[r], ' ');
      
      for (Slotinfo slot : slots) {
         int key     = slot.key();
         int dir     = Masker.Slot.dir(key);
         int clueIdx = Masker.Slot.clueIndex(key);
         
         int cr = SwedishGenerator.IT[clueIdx].r();
         int cc = SwedishGenerator.IT[clueIdx].c();
         
         // User requested: aAAAA for a four letter to RIGHT clue slot.
         // SwedishGenerator: 1=RIGHT, 0=DOWN, 2=UP, 3=LEFT
         char clueChar;
         char slotChar;
         switch (dir) {
            case 1:
               clueChar = 'a';
               slotChar = 'A';
               break; // RIGHT
            case 0:
               clueChar = 'b';
               slotChar = 'B';
               break; // DOWN
            case 2:
               clueChar = 'c';
               slotChar = 'C';
               break; // UP
            case 3:
               clueChar = 'd';
               slotChar = 'D';
               break; // LEFT
            default:
               clueChar = '?';
               slotChar = '?';
         }
         
         display[cr][cc] = clueChar;
         
         Masker.Slot.from(slot.key(), slot.lo(), slot.hi(), null).walk().forEach(idx -> {
            int r = SwedishGenerator.IT[idx].r();
            int c = SwedishGenerator.IT[idx].c();
            if (display[r][c] == ' ' || (display[r][c] >= 'A' && display[r][c] <= 'D')) {
               if (display[r][c] != ' ' && display[r][c] != slotChar) {
                  display[r][c] = '+'; // Intersection
               } else {
                  display[r][c] = slotChar;
               }
            }
         });
      }
      
      for (int r = 0; r < R; r++) {
         System.out.println("[DEBUG_LOG] " + new String(display[r]));
      }
   }
}
