package de.effigenix.loadtest.simulation;

import de.effigenix.loadtest.infrastructure.LoadTestInfrastructure;
import de.effigenix.loadtest.scenario.*;
import io.gatling.javaapi.core.Simulation;
import io.gatling.javaapi.http.HttpProtocolBuilder;

import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.http.HttpDsl.*;

/**
 * Haupt-Simulation: Gemischte Last über alle Szenarien.
 *
 * Lastprofil (simuliert ~2500 Requests ≈ komprimiertes Jahres-Volumen):
 *  - 15 Admin-User:          Stammdaten-CRUD           (ramp 30s)
 *  - 30 Produktions-Worker:  Produktions-Workflow      (ramp 60s, sustained)
 *  - 15 Lager-Arbeiter:      Lagerverwaltung           (ramp 30s)
 *  - 50 Read-Only-User:      Listen durchblättern      (ramp 60s)
 *
 * Assertions:
 *  - p95 < 500ms (global)
 *  - p99 < 1000ms (global)
 *  - Error-Rate < 5%
 */
public class FullWorkloadSimulation extends Simulation {

    // Infrastruktur muss vor der Feld-Initialisierung starten
    static {
        LoadTestInfrastructure.start();
    }

    @Override
    public void after() {
        LoadTestInfrastructure.stop();
    }

    HttpProtocolBuilder httpProtocol = http
            .baseUrl(LoadTestInfrastructure.baseUrl())
            .acceptHeader("application/json")
            .contentTypeHeader("application/json")
            .shareConnections();

    {
        setUp(
                // Admin-User: Stammdaten-CRUD
                MasterDataScenario.masterDataWorkflow()
                        .injectOpen(rampUsers(15).during(30)),

                // Produktions-Worker: Vollständiger Produktions-Workflow
                ProductionScenario.productionWorkflow()
                        .injectOpen(
                                rampUsers(10).during(30),
                                nothingFor(15),
                                rampUsers(10).during(30),
                                nothingFor(15),
                                rampUsers(10).during(30)
                        ),

                // Lager-Arbeiter: Lagerverwaltung
                InventoryScenario.inventoryWorkflow()
                        .injectOpen(rampUsers(15).during(30)),

                // Read-Only-User: Listen durchblättern
                ProductionScenario.productionReadOnly()
                        .injectOpen(
                                rampUsers(25).during(60),
                                nothingFor(15),
                                rampUsers(25).during(45)
                        ),

                // Zusätzliche Read-Only auf Stammdaten
                MasterDataScenario.masterDataReadOnly()
                        .injectOpen(
                                nothingFor(15),
                                rampUsers(25).during(45),
                                nothingFor(15),
                                rampUsers(25).during(45)
                        )
        ).protocols(httpProtocol)
                .assertions(
                        // Global: Login (BCrypt ~230ms) hebt den Schnitt
                        global().responseTime().percentile(95.0).lt(500),
                        global().responseTime().percentile(99.0).lt(1000),
                        global().failedRequests().percent().lt(5.0),

                        // Login darf langsam sein (BCrypt strength 12)
                        details("Login [admin]").responseTime().mean().lt(350),
                        details("Login [admin]").responseTime().percentile(95.0).lt(500),

                        // Einzeldatensatz-Reads: streng (mean < 20ms)
                        details("Rezept laden").responseTime().mean().lt(20),
                        details("Artikel laden").responseTime().mean().lt(20),
                        details("Lieferant laden").responseTime().mean().lt(20),
                        details("Kunde laden").responseTime().mean().lt(20),

                        // Listen mit wenig Daten (< 30 Einträge): mean < 35ms
                        details("Rezepte auflisten").responseTime().mean().lt(35),
                        details("Lagerorte auflisten").responseTime().mean().lt(35),
                        details("Bestände auflisten").responseTime().mean().lt(35),
                        details("Bestände unter Minimum").responseTime().mean().lt(35),
                        details("Bestände nach Lagerort").responseTime().mean().lt(35),
                        details("Lieferanten auflisten").responseTime().mean().lt(35),
                        details("Kategorien auflisten").responseTime().mean().lt(35),
                        details("Bestandsbewegungen auflisten").responseTime().mean().lt(35),
                        details("Bestandsbewegungen nach Bestand").responseTime().mean().lt(35),
                        details("Bestandsbewegungen nach Charge").responseTime().mean().lt(35),
                        details("Bestandsbewegungen nach Zeitraum").responseTime().mean().lt(35),
                        details("Inventuren auflisten").responseTime().mean().lt(35),
                        details("Inventur laden").responseTime().mean().lt(20),

                        // Listen mit viel Daten (50-300 Einträge): mean < 75ms
                        details("Chargen auflisten").responseTime().mean().lt(75),
                        details("Artikel auflisten").responseTime().mean().lt(75),
                        details("Kunden auflisten").responseTime().mean().lt(75),

                        // Garantiert vorkommende Write-Requests: moderat (mean < 50ms)
                        details("Charge planen").responseTime().mean().lt(50),
                        details("Charge starten").responseTime().mean().lt(50),
                        details("Charge abschließen").responseTime().mean().lt(50),
                        details("Produktionsauftrag anlegen").responseTime().mean().lt(50),
                        details("Produktionsauftrag freigeben").responseTime().mean().lt(50),
                        details("Produktionsauftrag abschließen").responseTime().mean().lt(50),
                        details("Produktionsauftrag stornieren").responseTime().mean().lt(50),
                        details("Produktionsauftrag umterminieren").responseTime().mean().lt(50),
                        details("Bestandsbewegung erfassen").responseTime().mean().lt(50),
                        details("Inventur anlegen").responseTime().mean().lt(50),
                        details("Inventur starten").responseTime().mean().lt(50),
                        details("Ist-Menge erfassen").responseTime().mean().lt(50),

                        // Tracing: BFS-Traversierung, mean < 50ms
                        details("Charge vorwärts tracen").responseTime().mean().lt(50),
                        details("Charge rückwärts tracen").responseTime().mean().lt(50),

                        // Produktionsaufträge-Listen: mean < 35ms
                        details("Produktionsaufträge auflisten").responseTime().mean().lt(35),
                        details("Produktionsaufträge nach Status").responseTime().mean().lt(35)
                );
    }
}
