package de.effigenix.loadtest.infrastructure;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.ConfigurableApplicationContext;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
 * Erzeugt realistische Testdaten via REST-API gegen die laufende Applikation.
 * Simuliert einen mittelgroßen Fleischerei-Betrieb mit Jahresproduktion.
 *
 * Datenmengen:
 *   15 Kategorien, 120 Artikel, 30 Lieferanten, 50 Kunden,
 *   8 Lagerorte, 30 Rezepte, ~1500 Chargen (5-Jahres-Produktion),
 *   100 Produktionsaufträge
 */
public final class LoadTestDataSeeder {

    private static final String ADMIN_USER = "admin";
    private static final String ADMIN_PASS = "admin123";

    private final String baseUrl;
    private final HttpClient http;
    private final ObjectMapper mapper;

    private String adminToken;

    // Gesammelte IDs für spätere Referenz in Szenarien
    private final List<String> categoryIds = new ArrayList<>();
    private final List<String> articleIds = new ArrayList<>();
    private final List<String> supplierIds = new ArrayList<>();
    private final List<String> customerIds = new ArrayList<>();
    private final List<String> storageLocationIds = new ArrayList<>();
    private final List<String> recipeIds = new ArrayList<>();
    private final List<String> batchIds = new ArrayList<>();
    private final List<String> productionOrderIds = new ArrayList<>();
    private final List<String> stockIds = new ArrayList<>();
    private final List<String> stockBatchIds = new ArrayList<>();
    private final List<String> stockMovementIds = new ArrayList<>();
    private final List<String> inventoryCountIds = new ArrayList<>();

    // Statische Felder für Zugriff aus Szenarien
    private static List<String> seededCategoryIds;
    private static List<String> seededArticleIds;
    private static List<String> seededSupplierIds;
    private static List<String> seededCustomerIds;
    private static List<String> seededStorageLocationIds;
    private static List<String> seededRecipeIds;
    private static List<String> seededBatchIds;
    private static List<String> seededProductionOrderIds;
    private static List<String> seededStockIds;
    private static List<String> seededStockBatchIds;
    private static List<String> seededStockMovementIds;
    private static List<String> seededInventoryCountIds;

    public LoadTestDataSeeder(ConfigurableApplicationContext appContext) {
        int port = appContext.getEnvironment()
                .getProperty("local.server.port", Integer.class, 8080);
        this.baseUrl = "http://localhost:" + port;
        this.http = HttpClient.newHttpClient();
        this.mapper = new ObjectMapper();
    }

    public void seed() {
        try {
            long start = System.currentTimeMillis();
            login();
            seedCategories();
            seedArticles();
            seedSuppliers();
            seedCustomers();
            seedStorageLocations();
            seedRecipes();
            seedBatchesForYear();
            seedProductionOrders();
            seedStocksAndBatches();
            seedStockMovements();
            seedInventoryCounts();

            // Statische Referenzen setzen
            seededCategoryIds = List.copyOf(categoryIds);
            seededArticleIds = List.copyOf(articleIds);
            seededSupplierIds = List.copyOf(supplierIds);
            seededCustomerIds = List.copyOf(customerIds);
            seededStorageLocationIds = List.copyOf(storageLocationIds);
            seededRecipeIds = List.copyOf(recipeIds);
            seededBatchIds = List.copyOf(batchIds);
            seededProductionOrderIds = List.copyOf(productionOrderIds);
            seededStockIds = List.copyOf(stockIds);
            seededStockBatchIds = List.copyOf(stockBatchIds);
            seededStockMovementIds = List.copyOf(stockMovementIds);
            seededInventoryCountIds = List.copyOf(inventoryCountIds);

            long duration = System.currentTimeMillis() - start;
            System.out.printf(
                    "Seeded in %dms: %d Kategorien, %d Artikel, %d Lieferanten, %d Kunden, "
                            + "%d Lagerorte, %d Rezepte, %d Chargen, %d Produktionsaufträge, "
                            + "%d Bestände, %d Bestandsbewegungen, %d Inventuren%n",
                    duration, categoryIds.size(), articleIds.size(), supplierIds.size(),
                    customerIds.size(), storageLocationIds.size(), recipeIds.size(),
                    batchIds.size(), productionOrderIds.size(),
                    stockIds.size(), stockMovementIds.size(), inventoryCountIds.size());
        } catch (Exception e) {
            throw new RuntimeException("Testdaten-Seeding fehlgeschlagen", e);
        }
    }

    private void login() throws Exception {
        String body = """
                {"username":"%s","password":"%s"}""".formatted(ADMIN_USER, ADMIN_PASS);
        var response = post("/api/auth/login", body, null);
        adminToken = mapper.readTree(response).get("accessToken").asText();
    }

    // ---- Kategorien (15) ----

    private void seedCategories() throws Exception {
        String[] categories = {
                "Frischfleisch", "Wurstwaren", "Schinken", "Aufschnitt",
                "Brühwurst", "Rohwurst", "Kochwurst", "Pasteten",
                "Convenience", "Gewürze & Zutaten", "Verpackungsmaterial",
                "Rohstoffe Schwein", "Rohstoffe Rind", "Geflügel", "Feinkost"
        };
        for (String name : categories) {
            String body = """
                    {"name":"%s","description":"Kategorie %s"}""".formatted(name, name);
            var json = mapper.readTree(post("/api/categories", body, adminToken));
            categoryIds.add(json.get("id").asText());
        }
    }

    // ---- Artikel (120) ----

    private int articleSeq = 1;

    private void seedArticles() throws Exception {
        // Eigene Produkte (Wurstwaren, Schinken, Aufschnitt, Brühwurst etc.)
        String[][] eigenprodukte = {
                {"Bratwurst grob", "1", "KG", "WEIGHT_BASED", "8.90"},
                {"Bratwurst fein", "1", "KG", "WEIGHT_BASED", "9.20"},
                {"Nürnberger Rostbratwurst", "1", "PIECE_FIXED", "FIXED", "4.50"},
                {"Currywurst", "1", "PIECE_FIXED", "FIXED", "3.90"},
                {"Wiener Würstchen", "4", "PIECE_FIXED", "FIXED", "3.50"},
                {"Bockwurst", "4", "PIECE_FIXED", "FIXED", "2.90"},
                {"Knackwurst", "4", "PIECE_FIXED", "FIXED", "3.20"},
                {"Weißwurst", "4", "PIECE_FIXED", "FIXED", "2.50"},
                {"Frankfurter Würstchen", "4", "PIECE_FIXED", "FIXED", "3.80"},
                {"Fleischwurst Ring", "4", "KG", "WEIGHT_BASED", "7.50"},
                {"Lyoner", "3", "KG", "WEIGHT_BASED", "8.20"},
                {"Bierschinken", "3", "KG", "WEIGHT_BASED", "8.50"},
                {"Jagdwurst", "3", "KG", "WEIGHT_BASED", "7.80"},
                {"Mortadella", "3", "KG", "WEIGHT_BASED", "9.20"},
                {"Schinkenwurst", "3", "KG", "WEIGHT_BASED", "9.50"},
                {"Salami Milano", "5", "KG", "WEIGHT_BASED", "22.50"},
                {"Salami Ungarn", "5", "KG", "WEIGHT_BASED", "19.80"},
                {"Cervelatwurst", "5", "KG", "WEIGHT_BASED", "18.90"},
                {"Mettwurst grob", "5", "KG", "WEIGHT_BASED", "10.50"},
                {"Mettwurst fein", "5", "KG", "WEIGHT_BASED", "11.20"},
                {"Teewurst", "5", "KG", "WEIGHT_BASED", "11.90"},
                {"Krakauer", "5", "KG", "WEIGHT_BASED", "9.80"},
                {"Paprikawurst", "5", "KG", "WEIGHT_BASED", "10.20"},
                {"Pfefferwurst", "5", "KG", "WEIGHT_BASED", "10.80"},
                {"Leberwurst fein", "6", "KG", "WEIGHT_BASED", "7.90"},
                {"Leberwurst grob", "6", "KG", "WEIGHT_BASED", "7.50"},
                {"Blutwurst", "6", "KG", "WEIGHT_BASED", "6.50"},
                {"Sülze", "6", "KG", "WEIGHT_BASED", "5.90"},
                {"Presssack", "6", "KG", "WEIGHT_BASED", "6.80"},
                {"Leberkäse", "7", "KG", "WEIGHT_BASED", "9.50"},
                {"Fleischkäse Pikant", "7", "KG", "WEIGHT_BASED", "10.20"},
                {"Pastete Champignon", "7", "KG", "WEIGHT_BASED", "12.50"},
                {"Pastete Pfeffer", "7", "KG", "WEIGHT_BASED", "13.20"},
                {"Schinken gekocht", "2", "KG", "WEIGHT_BASED", "18.90"},
                {"Nussschinken", "2", "KG", "WEIGHT_BASED", "22.50"},
                {"Lachsschinken", "2", "KG", "WEIGHT_BASED", "28.90"},
                {"Schwarzwälder Schinken", "2", "KG", "WEIGHT_BASED", "26.50"},
                {"Räucherschinken", "2", "KG", "WEIGHT_BASED", "21.90"},
                {"Kasseler Nacken", "2", "KG", "WEIGHT_BASED", "14.50"},
                {"Kasseler Rücken", "2", "KG", "WEIGHT_BASED", "15.90"},
                {"Fleischsalat", "14", "KG", "WEIGHT_BASED", "8.90"},
                {"Wurstsalat", "14", "KG", "WEIGHT_BASED", "9.50"},
                {"Kartoffelsalat m. Fleischeinlage", "14", "KG", "WEIGHT_BASED", "7.50"},
                {"Gulaschsuppe", "8", "KG", "WEIGHT_BASED", "6.50"},
                {"Maultaschen", "8", "PIECE_FIXED", "FIXED", "5.90"},
                {"Frikadellen", "8", "PIECE_FIXED", "FIXED", "1.80"},
                {"Hackfleisch Schwein/Rind", "0", "KG", "WEIGHT_BASED", "8.90"},
                {"Hackfleisch Rind", "0", "KG", "WEIGHT_BASED", "11.50"},
        };

        // Rohstoffe und Einkaufsartikel
        String[][] rohstoffe = {
                {"Schweinenacken", "11", "KG", "WEIGHT_BASED", "6.50"},
                {"Schweinebauch", "11", "KG", "WEIGHT_BASED", "5.20"},
                {"Schweinekeule", "11", "KG", "WEIGHT_BASED", "5.80"},
                {"Schweineschulter", "11", "KG", "WEIGHT_BASED", "5.50"},
                {"Schweinekopf", "11", "KG", "WEIGHT_BASED", "2.80"},
                {"Schweinerücken", "11", "KG", "WEIGHT_BASED", "7.20"},
                {"Schweineleber", "11", "KG", "WEIGHT_BASED", "3.50"},
                {"Schweineblut", "11", "KG", "WEIGHT_BASED", "1.80"},
                {"Schweinefett/Speck", "11", "KG", "WEIGHT_BASED", "3.20"},
                {"Schwarte", "11", "KG", "WEIGHT_BASED", "2.50"},
                {"Rinderfilet", "12", "KG", "WEIGHT_BASED", "45.00"},
                {"Rindernuss", "12", "KG", "WEIGHT_BASED", "16.50"},
                {"Rinderkeule", "12", "KG", "WEIGHT_BASED", "14.20"},
                {"Rinderschulter", "12", "KG", "WEIGHT_BASED", "12.80"},
                {"Rinderbrust", "12", "KG", "WEIGHT_BASED", "11.50"},
                {"Gulasch Rind", "12", "KG", "WEIGHT_BASED", "16.50"},
                {"Gulasch Schwein", "11", "KG", "WEIGHT_BASED", "10.90"},
                {"Hähnchenbrustfilet", "13", "KG", "WEIGHT_BASED", "15.90"},
                {"Hähnchenschenkel", "13", "KG", "WEIGHT_BASED", "6.50"},
                {"Putenbrust", "13", "KG", "WEIGHT_BASED", "14.50"},
                {"Putenkeule", "13", "KG", "WEIGHT_BASED", "8.90"},
                {"Lammkeule", "0", "KG", "WEIGHT_BASED", "22.90"},
                {"Lammrücken", "0", "KG", "WEIGHT_BASED", "28.50"},
                {"Kalbsschnitzel", "0", "KG", "WEIGHT_BASED", "25.90"},
                // Gewürze & Zutaten
                {"Nitritpökelsalz", "9", "KG", "WEIGHT_BASED", "2.50"},
                {"Pfeffer schwarz gemahlen", "9", "HUNDRED_GRAM", "WEIGHT_BASED", "3.20"},
                {"Pfeffer weiß gemahlen", "9", "HUNDRED_GRAM", "WEIGHT_BASED", "3.80"},
                {"Paprika edelsüß", "9", "HUNDRED_GRAM", "WEIGHT_BASED", "2.80"},
                {"Paprika rosenscharf", "9", "HUNDRED_GRAM", "WEIGHT_BASED", "3.10"},
                {"Knoblauch granuliert", "9", "HUNDRED_GRAM", "WEIGHT_BASED", "4.50"},
                {"Knoblauch frisch", "9", "KG", "WEIGHT_BASED", "8.90"},
                {"Zwiebeln", "9", "KG", "WEIGHT_BASED", "1.20"},
                {"Majoran gerebelt", "9", "HUNDRED_GRAM", "WEIGHT_BASED", "3.90"},
                {"Kümmel gemahlen", "9", "HUNDRED_GRAM", "WEIGHT_BASED", "2.90"},
                {"Kümmel ganz", "9", "HUNDRED_GRAM", "WEIGHT_BASED", "2.50"},
                {"Senfkörner", "9", "HUNDRED_GRAM", "WEIGHT_BASED", "1.80"},
                {"Muskatnuss gemahlen", "9", "HUNDRED_GRAM", "WEIGHT_BASED", "5.50"},
                {"Koriander gemahlen", "9", "HUNDRED_GRAM", "WEIGHT_BASED", "2.80"},
                {"Ingwer gemahlen", "9", "HUNDRED_GRAM", "WEIGHT_BASED", "3.20"},
                {"Zucker", "9", "KG", "WEIGHT_BASED", "1.50"},
                {"Dextrose", "9", "KG", "WEIGHT_BASED", "2.80"},
                {"Eiswasser", "9", "KG", "WEIGHT_BASED", "0.10"},
                {"Phosphat E450", "9", "KG", "WEIGHT_BASED", "4.50"},
                {"Ascorbinsäure E300", "9", "KG", "WEIGHT_BASED", "12.00"},
                {"Starterkulturen", "9", "HUNDRED_GRAM", "WEIGHT_BASED", "18.50"},
                {"Rauchsalz", "9", "KG", "WEIGHT_BASED", "5.50"},
                // Verpackung & Därme
                {"Schweinedarm 28/30", "10", "PIECE_FIXED", "FIXED", "15.00"},
                {"Schweinedarm 32/34", "10", "PIECE_FIXED", "FIXED", "16.50"},
                {"Schafsdarm 22/24", "10", "PIECE_FIXED", "FIXED", "22.00"},
                {"Kunstdarm 45mm", "10", "PIECE_FIXED", "FIXED", "6.50"},
                {"Kunstdarm 60mm", "10", "PIECE_FIXED", "FIXED", "8.50"},
                {"Kunstdarm 90mm", "10", "PIECE_FIXED", "FIXED", "10.00"},
                {"Netz Schweinenetz", "10", "PIECE_FIXED", "FIXED", "3.50"},
                {"Vakuumbeutel 20x30", "10", "PIECE_FIXED", "FIXED", "0.15"},
                {"Vakuumbeutel 30x40", "10", "PIECE_FIXED", "FIXED", "0.22"},
                {"MAP-Schale 500g", "10", "PIECE_FIXED", "FIXED", "0.35"},
                {"MAP-Schale 250g", "10", "PIECE_FIXED", "FIXED", "0.28"},
                {"Etiketten Rolle 1000 St", "10", "PIECE_FIXED", "FIXED", "25.00"},
                // Frischfleisch zum Verkauf
                {"Schnitzel Schwein", "0", "KG", "WEIGHT_BASED", "12.90"},
                {"Rouladen Rind", "0", "KG", "WEIGHT_BASED", "18.50"},
                {"Braten Schwein", "0", "KG", "WEIGHT_BASED", "11.50"},
                {"Spare Ribs", "0", "KG", "WEIGHT_BASED", "13.90"},
                {"Kotelett Schwein", "0", "KG", "WEIGHT_BASED", "10.90"},
                {"Filet Schwein", "0", "KG", "WEIGHT_BASED", "14.50"},
                {"Tafelspitz", "12", "KG", "WEIGHT_BASED", "19.90"},
                {"Suppenfleisch Rind", "12", "KG", "WEIGHT_BASED", "9.50"},
                {"Beinscheibe Rind", "12", "KG", "WEIGHT_BASED", "8.50"},
                {"Ochsenschwanz", "12", "KG", "WEIGHT_BASED", "12.50"},
        };

        for (String[][] batch : List.of(eigenprodukte, rohstoffe)) {
            for (String[] a : batch) {
                String catId = categoryIds.get(Integer.parseInt(a[1]) % categoryIds.size());
                String num = "ART-%05d".formatted(articleSeq++);
                String body = """
                        {"name":"%s","articleNumber":"%s","categoryId":"%s","unit":"%s","priceModel":"%s","price":%s}"""
                        .formatted(a[0], num, catId, a[2], a[3], a[4]);
                var json = mapper.readTree(post("/api/articles", body, adminToken));
                articleIds.add(json.get("id").asText());
            }
        }
    }

    // ---- Lieferanten (30) ----

    private void seedSuppliers() throws Exception {
        String[][] suppliers = {
                {"Fleischgroßhandel Müller", "0711-1234567"},
                {"Gewürzkontor Hamburg", "040-9876543"},
                {"Darmfabrik Süd", "089-5551234"},
                {"Bio-Fleisch Bauer Schmidt", "07151-8889999"},
                {"Metzgereibedarf Weber", "0621-3334444"},
                {"Nordisch Fleisch GmbH", "040-1112233"},
                {"Schwäbische Landfleisch", "0711-5556677"},
                {"Kräuter & Gewürze Maier", "089-4445566"},
                {"Verpackung Express", "0621-7778899"},
                {"Kühl-Logistik Klein", "0711-2223344"},
                {"Rinderzucht Oberbayern", "08821-112233"},
                {"Geflügelhof Sonnental", "07156-445566"},
                {"Wurstgewürz Spezialitäten", "0911-778899"},
                {"Hygiene-Bedarf Reinhardt", "0621-990011"},
                {"Maschinenservice Fischer", "0711-667788"},
                {"Verpackungsmaterial Nord", "040-223344"},
                {"Lammfleisch Alpenland", "08821-556677"},
                {"Wild & Geflügel Jäger", "07531-889900"},
                {"Naturgewürze Bio", "089-112244"},
                {"Fleisch-Import Europa", "069-334455"},
                {"Schlachthof Reutlingen", "07121-556677"},
                {"Gewürzhaus Orient", "0711-998877"},
                {"Bayerische Fleischwerke", "089-665544"},
                {"Darmhandel International", "040-332211"},
                {"Fleischgroßmarkt Frankfurt", "069-112233"},
                {"Verpackungswerk Heilbronn", "07131-443322"},
                {"Salzhandel Südwest", "0711-887766"},
                {"Starter-Kulturen Wagner", "0621-554433"},
                {"Tiefkühl-Logistik Schwab", "07151-332211"},
                {"Reinigungsmittel Haas", "0711-776655"},
        };
        for (String[] s : suppliers) {
            String body = """
                    {"name":"%s","phone":"%s"}""".formatted(s[0], s[1]);
            var json = mapper.readTree(post("/api/suppliers", body, adminToken));
            supplierIds.add(json.get("id").asText());
        }
    }

    // ---- Kunden (50) ----

    private void seedCustomers() throws Exception {
        String[][] customers = {
                // Gastronomie B2B
                {"Restaurant Zum Goldenen Hirsch", "B2B", "Königstraße 12", "70173", "Stuttgart"},
                {"Gasthaus Linde", "B2B", "Hauptstraße 5", "70178", "Stuttgart"},
                {"Hotel Schwabenstube", "B2B", "Schlossplatz 8", "70173", "Stuttgart"},
                {"Pizzeria Bella Italia", "B2B", "Marienstraße 22", "70178", "Stuttgart"},
                {"Restaurant Ochsen", "B2B", "Calwer Str. 31", "70173", "Stuttgart"},
                {"Gasthof Adler", "B2B", "Tübinger Str. 44", "70178", "Stuttgart"},
                {"Restaurant Asia Garden", "B2B", "Eberhardstr. 10", "70173", "Stuttgart"},
                {"Steakhouse El Gaucho", "B2B", "Rotebühlplatz 1", "70178", "Stuttgart"},
                {"Weinwirtschaft Zur Kiste", "B2B", "Kanalstr. 2", "70182", "Stuttgart"},
                {"Brauerei Schönbuch", "B2B", "Böblinger Str. 101", "71032", "Böblingen"},
                {"Hotel Pullman", "B2B", "Heilbronner Str. 88", "70191", "Stuttgart"},
                {"Restaurant Zeppelin", "B2B", "Arnulf-Klett-Platz 7", "70173", "Stuttgart"},
                // Kantinen B2B
                {"Betriebskantine Bosch", "B2B", "Robert-Bosch-Str. 1", "70839", "Gerlingen"},
                {"Betriebskantine Daimler", "B2B", "Mercedesstr. 120", "70372", "Stuttgart"},
                {"Betriebskantine Porsche", "B2B", "Porschestr. 42", "70435", "Stuttgart"},
                {"Uni-Mensa Stuttgart", "B2B", "Pfaffenwaldring 45", "70569", "Stuttgart"},
                {"Kantine Landratsamt", "B2B", "Löwentorstr. 54", "70376", "Stuttgart"},
                // Einzelhandel B2B
                {"Metzgerei-Filiale Mitte", "B2B", "Marktplatz 3", "70173", "Stuttgart"},
                {"Metzgerei-Filiale Vaihingen", "B2B", "Vaihinger Markt 8", "70563", "Stuttgart"},
                {"Metzgerei-Filiale Degerloch", "B2B", "Epplestr. 12", "70597", "Stuttgart"},
                {"Metzgerei-Filiale Fellbach", "B2B", "Bahnhofstr. 24", "70734", "Fellbach"},
                {"Metzgerei-Filiale Esslingen", "B2B", "Pliensaustr. 6", "73728", "Esslingen"},
                {"Bio-Laden Sonnenschein", "B2B", "Gutenbergstr. 15", "70176", "Stuttgart"},
                {"Feinkost Böhm", "B2B", "Calwer Str. 58", "70173", "Stuttgart"},
                {"Reformhaus Vital", "B2B", "Schulstr. 3", "70173", "Stuttgart"},
                {"Hofladen Streuobstwiese", "B2B", "Gartenstr. 7", "70771", "Leinfelden"},
                {"Wochenmarkt Stuttgart", "B2B", "Schillerplatz", "70173", "Stuttgart"},
                // Großkunden B2B
                {"Großmarkt Stuttgart", "B2B", "Heilbronner Str. 320", "70469", "Stuttgart"},
                {"Edeka Großhandel Süd", "B2B", "Hedelfinger Str. 55", "70327", "Stuttgart"},
                {"REWE Markt Stuttgart-Ost", "B2B", "Neckarstr. 190", "70190", "Stuttgart"},
                {"Tegut Stuttgart", "B2B", "Eberhardstr. 35", "70173", "Stuttgart"},
                {"Catering König", "B2B", "Industriestr. 22", "70565", "Stuttgart"},
                {"Event-Catering Schwab", "B2B", "Ulmer Str. 10", "70188", "Stuttgart"},
                {"Party-Service Müller", "B2B", "Pragstr. 44", "70376", "Stuttgart"},
                {"Essen auf Rädern Süd", "B2B", "Böblinger Str. 22", "70199", "Stuttgart"},
                {"Krankenhaus-Küche Olgahospital", "B2B", "Kriegsbergstr. 60", "70174", "Stuttgart"},
                {"Seniorenheim Am Park", "B2B", "Parkweg 5", "70192", "Stuttgart"},
                {"Kindergarten-Catering Sonnenkind", "B2B", "Rosensteinstr. 12", "70191", "Stuttgart"},
                {"Flughafen-Gastronomie STR", "B2B", "Flughafenstr. 43", "70629", "Stuttgart"},
                // Privatkunden B2C
                {"Max Mustermann", "B2C", "Musterweg 1", "70173", "Stuttgart"},
                {"Erika Musterfrau", "B2C", "Beispielgasse 7", "70178", "Stuttgart"},
                {"Hans Wurstliebhaber", "B2C", "Fleischstr. 12", "70182", "Stuttgart"},
                {"Familie Schmid", "B2C", "Gartenweg 3", "70563", "Stuttgart"},
                {"Thomas Grillfan", "B2C", "Sonnenhalde 5", "70597", "Stuttgart"},
                {"Andrea Koch", "B2C", "Küchenstr. 8", "70376", "Stuttgart"},
                {"Peter Großeinkauf", "B2C", "Vorratsweg 22", "70435", "Stuttgart"},
                {"Susanne Feinschmecker", "B2C", "Delikatessenstr. 1", "70173", "Stuttgart"},
                {"Familie Weber", "B2C", "Am Weinberg 15", "70734", "Fellbach"},
                {"Familie Bauer", "B2C", "Lindenstr. 30", "73728", "Esslingen"},
                {"Verein SV Stuttgart 08", "B2C", "Sportplatzweg 1", "70469", "Stuttgart"},
        };
        int phoneSeq = 1000000;
        for (String[] c : customers) {
            String phone = "0711-%07d".formatted(phoneSeq++);
            String body = """
                    {"name":"%s","type":"%s","street":"%s","postalCode":"%s","city":"%s","country":"DE","phone":"%s"}"""
                    .formatted(c[0], c[1], c[2], c[3], c[4], phone);
            var json = mapper.readTree(post("/api/customers", body, adminToken));
            customerIds.add(json.get("id").asText());
        }
    }

    // ---- Lagerorte (8) ----

    private void seedStorageLocations() throws Exception {
        String[][] locations = {
                {"Kühlhaus 1", "COLD_ROOM", "0", "4"},
                {"Kühlhaus 2", "COLD_ROOM", "0", "4"},
                {"Tiefkühllager", "FREEZER", "-22", "-18"},
                {"Tiefkühllager Reserven", "FREEZER", "-22", "-18"},
                {"Trockenlager", "DRY_STORAGE", null, null},
                {"Gewürzlager", "DRY_STORAGE", null, null},
                {"Theke Verkauf", "DISPLAY_COUNTER", "0", "7"},
                {"Produktionshalle", "PRODUCTION_AREA", "10", "18"},
        };
        for (String[] loc : locations) {
            StringBuilder sb = new StringBuilder();
            sb.append("{\"name\":\"").append(loc[0]).append("\",\"storageType\":\"").append(loc[1]).append("\"");
            if (loc[2] != null) {
                sb.append(",\"minTemperature\":\"").append(loc[2]).append("\"");
                sb.append(",\"maxTemperature\":\"").append(loc[3]).append("\"");
            }
            sb.append("}");
            var json = mapper.readTree(post("/api/inventory/storage-locations", sb.toString(), adminToken));
            storageLocationIds.add(json.get("id").asText());
        }
    }

    // ---- Rezepte (30) ----

    private void seedRecipes() throws Exception {
        String[][] recipes = {
                // Brühwürste
                {"Bratwurst grob", "FINISHED_PRODUCT", "88", "10", "10", "KILOGRAM"},
                {"Bratwurst fein", "FINISHED_PRODUCT", "90", "10", "10", "KILOGRAM"},
                {"Wiener Würstchen", "FINISHED_PRODUCT", "85", "14", "20", "KILOGRAM"},
                {"Bockwurst", "FINISHED_PRODUCT", "86", "14", "15", "KILOGRAM"},
                {"Fleischwurst Ring", "FINISHED_PRODUCT", "87", "10", "25", "KILOGRAM"},
                {"Lyoner", "FINISHED_PRODUCT", "88", "10", "20", "KILOGRAM"},
                {"Bierschinken", "FINISHED_PRODUCT", "82", "14", "15", "KILOGRAM"},
                {"Jagdwurst", "FINISHED_PRODUCT", "85", "10", "20", "KILOGRAM"},
                // Rohwürste
                {"Salami Milano", "FINISHED_PRODUCT", "62", "28", "10", "KILOGRAM"},
                {"Cervelatwurst", "FINISHED_PRODUCT", "65", "21", "10", "KILOGRAM"},
                {"Mettwurst grob", "FINISHED_PRODUCT", "75", "14", "8", "KILOGRAM"},
                {"Teewurst", "FINISHED_PRODUCT", "78", "7", "5", "KILOGRAM"},
                // Kochwürste
                {"Leberwurst fein", "FINISHED_PRODUCT", "80", "7", "10", "KILOGRAM"},
                {"Leberwurst grob", "FINISHED_PRODUCT", "82", "7", "10", "KILOGRAM"},
                {"Blutwurst", "FINISHED_PRODUCT", "85", "7", "10", "KILOGRAM"},
                {"Sülze", "FINISHED_PRODUCT", "90", "7", "15", "KILOGRAM"},
                // Schinken
                {"Schinken gekocht", "FINISHED_PRODUCT", "72", "21", "12", "KILOGRAM"},
                {"Nussschinken", "FINISHED_PRODUCT", "68", "28", "10", "KILOGRAM"},
                {"Schwarzwälder Schinken", "FINISHED_PRODUCT", "55", "42", "8", "KILOGRAM"},
                {"Kasseler Nacken", "FINISHED_PRODUCT", "75", "14", "15", "KILOGRAM"},
                // Pasteten & Convenience
                {"Leberkäse", "FINISHED_PRODUCT", "90", "3", "25", "KILOGRAM"},
                {"Fleischkäse Pikant", "FINISHED_PRODUCT", "88", "3", "20", "KILOGRAM"},
                {"Frikadellen", "FINISHED_PRODUCT", "92", "5", "30", "KILOGRAM"},
                {"Maultaschen", "FINISHED_PRODUCT", "85", "7", "20", "KILOGRAM"},
                // Halbfertigprodukte (Intermediate)
                {"Brät fein", "INTERMEDIATE", "95", "1", "10", "KILOGRAM"},
                {"Brät grob", "INTERMEDIATE", "95", "1", "10", "KILOGRAM"},
                {"Gewürzmischung Bratwurst", "INTERMEDIATE", "100", "180", "2", "KILOGRAM"},
                {"Gewürzmischung Salami", "INTERMEDIATE", "100", "180", "2", "KILOGRAM"},
                {"Pökel-Lake Standard", "INTERMEDIATE", "100", "14", "50", "KILOGRAM"},
                {"Hackfleisch gewürzt", "INTERMEDIATE", "95", "1", "5", "KILOGRAM"},
        };

        for (int i = 0; i < recipes.length; i++) {
            String outputArticleId = articleIds.get(i % articleIds.size());
            String body = """
                    {"name":"%s","version":1,"type":"%s","description":"Rezept für %s","yieldPercentage":%s,"shelfLifeDays":%s,"outputQuantity":"%s","outputUom":"%s","articleId":"%s"}"""
                    .formatted(recipes[i][0], recipes[i][1], recipes[i][0],
                            recipes[i][2], recipes[i][3], recipes[i][4], recipes[i][5], outputArticleId);
            var json = mapper.readTree(post("/api/recipes", body, adminToken));
            String recipeId = json.get("id").asText();
            recipeIds.add(recipeId);

            // 3-5 Zutaten pro Rezept (Rohstoffe aus Artikel-Pool)
            int ingredientCount = 3 + (i % 3);
            int rohstoffStart = 48; // Index der Rohstoff-Artikel
            for (int j = 0; j < ingredientCount; j++) {
                String ingredientArticleId = articleIds.get(rohstoffStart + ((i * 5 + j) % (articleIds.size() - rohstoffStart)));
                String qty = "%.1f".formatted(0.5 + (j + 1) * 0.8);
                String ingredientBody = """
                        {"position":%d,"articleId":"%s","quantity":"%s","uom":"KILOGRAM","substitutable":false}"""
                        .formatted(j + 1, ingredientArticleId, qty);
                post("/api/recipes/" + recipeId + "/ingredients", ingredientBody, adminToken);
            }

            // 2-4 Produktionsschritte
            String[][] steps = {
                    {"Rohstoffe vorbereiten und wiegen", "15", "18"},
                    {"Wolfen/Kuttern/Mischen", "30", "4"},
                    {"Füllen und Abdrehen/Formen", "25", "12"},
                    {"Brühen/Räuchern/Reifen", "60", "75"},
            };
            int stepCount = 2 + (i % 3);
            for (int s = 0; s < stepCount && s < steps.length; s++) {
                String stepBody = """
                        {"stepNumber":%d,"description":"%s","durationMinutes":%s,"temperatureCelsius":%s}"""
                        .formatted(s + 1, steps[s][0], steps[s][1], steps[s][2]);
                post("/api/recipes/" + recipeId + "/steps", stepBody, adminToken);
            }

            // Rezept aktivieren
            post("/api/recipes/" + recipeId + "/activate", "{}", adminToken);
        }
    }

    // ---- Chargen: 5 Jahre Produktion (~1500 Chargen) ----

    private void seedBatchesForYear() throws Exception {
        var today = LocalDate.now();
        var fiveYearsAgo = today.minusYears(5);
        var rnd = ThreadLocalRandom.current();
        long totalDays = java.time.temporal.ChronoUnit.DAYS.between(fiveYearsAgo, today);

        // ~1500 Chargen über 5 Jahre verteilt (~1,2 pro Arbeitstag)
        int batchCount = 1500;
        int logged = 0;
        for (int i = 0; i < batchCount; i++) {
            LocalDate productionDate = fiveYearsAgo.plusDays(rnd.nextLong(totalDays));
            LocalDate bestBefore = productionDate.plusDays(rnd.nextInt(7, 90));

            String recipeId = recipeIds.get(rnd.nextInt(recipeIds.size()));
            int qty = rnd.nextInt(5, 50);

            String body = """
                    {"recipeId":"%s","plannedQuantity":"%d","plannedQuantityUnit":"KILOGRAM","productionDate":"%s","bestBeforeDate":"%s"}"""
                    .formatted(recipeId, qty, productionDate, bestBefore);

            try {
                var json = mapper.readTree(post("/api/production/batches", body, adminToken));
                String batchId = json.get("id").asText();
                batchIds.add(batchId);

                // 70% der Chargen starten
                if (rnd.nextDouble() < 0.70) {
                    tryPost("/api/production/batches/" + batchId + "/start", "{}", adminToken);
                }
            } catch (Exception e) {
                // Einzelne Fehler ignorieren (z.B. Duplikat-Batch-Nummern)
            }

            // Fortschritt loggen
            if ((i + 1) % 500 == 0) {
                System.out.printf("  Chargen-Seeding: %d/%d ...%n", i + 1, batchCount);
            }
        }
    }

    // ---- Produktionsaufträge (100, ab heute geplant) ----

    private void seedProductionOrders() throws Exception {
        var today = LocalDate.now();
        var rnd = ThreadLocalRandom.current();
        String[] priorities = {"NORMAL", "NORMAL", "NORMAL", "URGENT"};

        for (int i = 0; i < 100; i++) {
            LocalDate plannedDate = today.plusDays(rnd.nextInt(0, 90));
            String recipeId = recipeIds.get(rnd.nextInt(recipeIds.size()));
            int qty = rnd.nextInt(10, 100);
            String priority = priorities[rnd.nextInt(priorities.length)];

            String body = """
                    {"recipeId":"%s","plannedQuantity":"%d","plannedQuantityUnit":"KILOGRAM","plannedDate":"%s","priority":"%s","notes":"Seeded Lasttest-Auftrag #%d"}"""
                    .formatted(recipeId, qty, plannedDate, priority, i + 1);

            try {
                var json = mapper.readTree(post("/api/production/production-orders", body, adminToken));
                String orderId = json.get("id").asText();
                productionOrderIds.add(orderId);

                // 40% der Aufträge sofort freigeben
                if (rnd.nextDouble() < 0.40) {
                    tryPost("/api/production/production-orders/" + orderId + "/release", "{}", adminToken);
                }
            } catch (Exception e) {
                // Einzelne Fehler ignorieren
            }
        }
    }

    // ---- Bestände & Chargen (20 Stocks mit je 2-3 Batches) ----

    private void seedStocksAndBatches() throws Exception {
        var rnd = ThreadLocalRandom.current();
        var today = LocalDate.now();

        // 20 Bestände: jeweils ein Artikel an einem Lagerort
        int stockCount = Math.min(20, articleIds.size());
        for (int i = 0; i < stockCount; i++) {
            String articleId = articleIds.get(i);
            String locationId = storageLocationIds.get(i % storageLocationIds.size());

            String body = """
                    {"articleId":"%s","storageLocationId":"%s","minimumLevelAmount":"5.0","minimumLevelUnit":"KILOGRAM"}"""
                    .formatted(articleId, locationId);

            try {
                var json = mapper.readTree(post("/api/inventory/stocks", body, adminToken));
                String stockId = json.get("id").asText();
                stockIds.add(stockId);

                // 2-3 Batches pro Stock
                int batchCount = 2 + (i % 2);
                for (int j = 0; j < batchCount; j++) {
                    String batchType = (j % 2 == 0) ? "PRODUCED" : "PURCHASED";
                    String batchRef = "CHARGE-%05d".formatted(i * 10 + j);
                    LocalDate expiry = today.plusDays(rnd.nextInt(14, 180));
                    int qty = rnd.nextInt(5, 50);

                    String batchBody = """
                            {"batchId":"%s","batchType":"%s","quantityAmount":"%d.0","quantityUnit":"KILOGRAM","expiryDate":"%s"}"""
                            .formatted(batchRef, batchType, qty, expiry);

                    try {
                        var batchJson = mapper.readTree(
                                post("/api/inventory/stocks/" + stockId + "/batches", batchBody, adminToken));
                        stockBatchIds.add(batchJson.get("id").asText());
                    } catch (Exception ignored) {}
                }
            } catch (Exception e) {
                // Stock-Duplikat (gleicher Artikel/Lagerort) ignorieren
            }
        }
    }

    // ---- Bestandsbewegungen (50 Movements) ----

    private void seedStockMovements() throws Exception {
        if (stockIds.isEmpty() || stockBatchIds.isEmpty()) return;

        var rnd = ThreadLocalRandom.current();
        String[] movementTypes = {"GOODS_RECEIPT", "PRODUCTION_OUTPUT", "PRODUCTION_CONSUMPTION", "SALE", "RETURN"};

        for (int i = 0; i < 50; i++) {
            int stockIdx = i % stockIds.size();
            String stockId = stockIds.get(stockIdx);
            String articleId = articleIds.get(stockIdx);
            String stockBatchId = stockBatchIds.get(i % stockBatchIds.size());
            String batchRef = "CHARGE-%05d".formatted(i);
            String batchType = (i % 2 == 0) ? "PRODUCED" : "PURCHASED";
            String movementType = movementTypes[rnd.nextInt(movementTypes.length)];
            int qty = rnd.nextInt(1, 20);

            String body = """
                    {"stockId":"%s","articleId":"%s","stockBatchId":"%s","batchId":"%s","batchType":"%s","movementType":"%s","quantityAmount":"%d.0","quantityUnit":"KILOGRAM"}"""
                    .formatted(stockId, articleId, stockBatchId, batchRef, batchType, movementType, qty);

            try {
                var json = mapper.readTree(post("/api/inventory/stock-movements", body, adminToken));
                stockMovementIds.add(json.get("id").asText());
            } catch (Exception ignored) {}
        }
    }

    // ---- Inventuren (3 pro Lagerort mit Stocks, davon 2 gestartet) ----

    private void seedInventoryCounts() throws Exception {
        if (storageLocationIds.isEmpty() || stockIds.isEmpty()) return;

        // Nur Lagerorte nutzen, die auch Stocks haben (maximal 3 Lagerorte)
        int countLimit = Math.min(3, storageLocationIds.size());
        for (int i = 0; i < countLimit; i++) {
            String locationId = storageLocationIds.get(i);

            String body = """
                    {"storageLocationId":"%s","countDate":"%s"}"""
                    .formatted(locationId, LocalDate.now());

            try {
                var json = mapper.readTree(post("/api/inventory/inventory-counts", body, adminToken));
                String countId = json.get("id").asText();
                inventoryCountIds.add(countId);

                // 2 von 3 Inventuren starten → Status COUNTING
                if (i < 2) {
                    tryPatch("/api/inventory/inventory-counts/" + countId + "/start", adminToken);
                }
            } catch (Exception ignored) {}
        }
    }

    // ---- HTTP Helper ----

    private String post(String path, String jsonBody, String token) throws Exception {
        var builder = HttpRequest.newBuilder()
                .uri(URI.create(baseUrl + path))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(jsonBody));
        if (token != null) {
            builder.header("Authorization", "Bearer " + token);
        }
        var response = http.send(builder.build(), HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() >= 400) {
            throw new RuntimeException("HTTP %d auf %s: %s".formatted(
                    response.statusCode(), path, response.body()));
        }
        return response.body();
    }

    /** Post ohne Exception bei Fehler – für optionale Status-Übergänge. */
    private void tryPost(String path, String jsonBody, String token) {
        try {
            post(path, jsonBody, token);
        } catch (Exception ignored) {
            // Status-Übergang fehlgeschlagen, z.B. wegen Vorbedingungen
        }
    }

    /** Patch ohne Exception bei Fehler – für optionale Status-Übergänge. */
    private void tryPatch(String path, String token) {
        try {
            patch(path, token);
        } catch (Exception ignored) {
            // Status-Übergang fehlgeschlagen, z.B. wegen Vorbedingungen
        }
    }

    private String patch(String path, String token) throws Exception {
        var builder = HttpRequest.newBuilder()
                .uri(URI.create(baseUrl + path))
                .header("Content-Type", "application/json")
                .method("PATCH", HttpRequest.BodyPublishers.ofString("{}"));
        if (token != null) {
            builder.header("Authorization", "Bearer " + token);
        }
        var response = http.send(builder.build(), HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() >= 400) {
            throw new RuntimeException("HTTP %d auf %s: %s".formatted(
                    response.statusCode(), path, response.body()));
        }
        return response.body();
    }

    // ---- Statische Getter für Szenarien ----

    public static List<String> categoryIds() { return seededCategoryIds; }
    public static List<String> articleIds() { return seededArticleIds; }
    public static List<String> supplierIds() { return seededSupplierIds; }
    public static List<String> customerIds() { return seededCustomerIds; }
    public static List<String> storageLocationIds() { return seededStorageLocationIds; }
    public static List<String> recipeIds() { return seededRecipeIds; }
    public static List<String> batchIds() { return seededBatchIds; }
    public static List<String> productionOrderIds() { return seededProductionOrderIds; }
    public static List<String> stockIds() { return seededStockIds; }
    public static List<String> stockBatchIds() { return seededStockBatchIds; }
    public static List<String> stockMovementIds() { return seededStockMovementIds; }
    public static List<String> inventoryCountIds() { return seededInventoryCountIds; }
}
