package de.effigenix.loadtest.scenario;

import de.effigenix.loadtest.infrastructure.LoadTestDataSeeder;
import de.effigenix.loadtest.util.JsonBodyBuilder;
import io.gatling.javaapi.core.ChainBuilder;
import io.gatling.javaapi.core.ScenarioBuilder;

import java.util.concurrent.ThreadLocalRandom;

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

/**
 * Produktions-Szenario: Rezepte, Chargen, Produktionsaufträge.
 * Bildet den End-to-End-Workflow der Produktion ab.
 */
public final class ProductionScenario {

    private ProductionScenario() {}

    // ---- Rezepte ----

    public static ChainBuilder listRecipes() {
        return exec(
                http("Rezepte auflisten")
                        .get("/api/recipes")
                        .header("Authorization", "Bearer #{accessToken}")
                        .check(status().is(200))
        );
    }

    public static ChainBuilder getRandomRecipe() {
        return exec(session -> {
            var ids = LoadTestDataSeeder.recipeIds();
            String id = ids.get(ThreadLocalRandom.current().nextInt(ids.size()));
            return session.set("recipeId", id);
        }).exec(
                http("Rezept laden")
                        .get("/api/recipes/#{recipeId}")
                        .header("Authorization", "Bearer #{accessToken}")
                        .check(status().is(200))
        );
    }

    // ---- Chargen (Batches) ----

    public static ChainBuilder listBatches() {
        return exec(
                http("Chargen auflisten")
                        .get("/api/production/batches")
                        .header("Authorization", "Bearer #{accessToken}")
                        .check(status().is(200))
        );
    }

    public static ChainBuilder planBatch() {
        return exec(session -> {
            var ids = LoadTestDataSeeder.recipeIds();
            String recipeId = ids.get(ThreadLocalRandom.current().nextInt(ids.size()));
            return session.set("planBatchBody", JsonBodyBuilder.planBatchBody(recipeId));
        }).exec(
                http("Charge planen")
                        .post("/api/production/batches")
                        .header("Authorization", "Bearer #{accessToken}")
                        .header("Content-Type", "application/json")
                        .body(StringBody("#{planBatchBody}"))
                        .check(status().in(200, 201, 400, 409, 500))
                        .check(jsonPath("$.id").optional().saveAs("batchId"))
        );
    }

    public static ChainBuilder startBatch() {
        return exec(
                http("Charge starten")
                        .post("/api/production/batches/#{batchId}/start")
                        .header("Authorization", "Bearer #{accessToken}")
                        .check(status().in(200, 400, 409, 500))
        );
    }

    public static ChainBuilder completeBatch() {
        return exec(
                http("Charge abschließen")
                        .post("/api/production/batches/#{batchId}/complete")
                        .header("Authorization", "Bearer #{accessToken}")
                        .header("Content-Type", "application/json")
                        .body(StringBody(JsonBodyBuilder.completeBatchBody()))
                        .check(status().in(200, 400, 409, 500))
        );
    }

    // ---- Produktionsaufträge ----

    public static ChainBuilder createProductionOrder() {
        return exec(session -> {
            var ids = LoadTestDataSeeder.recipeIds();
            String recipeId = ids.get(ThreadLocalRandom.current().nextInt(ids.size()));
            return session.set("prodOrderBody", JsonBodyBuilder.createProductionOrderBody(recipeId));
        }).exec(
                http("Produktionsauftrag anlegen")
                        .post("/api/production/production-orders")
                        .header("Authorization", "Bearer #{accessToken}")
                        .header("Content-Type", "application/json")
                        .body(StringBody("#{prodOrderBody}"))
                        .check(status().in(200, 201, 400, 409, 500))
                        .check(jsonPath("$.id").optional().saveAs("productionOrderId"))
        );
    }

    public static ChainBuilder releaseProductionOrder() {
        return exec(
                http("Produktionsauftrag freigeben")
                        .post("/api/production/production-orders/#{productionOrderId}/release")
                        .header("Authorization", "Bearer #{accessToken}")
                        .check(status().in(200, 400, 409, 500))
        );
    }

    // ---- Zusammengesetztes Szenario ----

    /**
     * Produktions-Workflow: Rezepte lesen → Charge planen → starten → abschließen.
     */
    public static ScenarioBuilder productionWorkflow() {
        return scenario("Produktions-Workflow")
                .exec(AuthenticationScenario.login("admin", "admin123"))
                .exec(listRecipes())
                .pause(1, 2)
                .exec(getRandomRecipe())
                .pause(1, 2)
                // Charge planen und durchlaufen (nur wenn planBatch erfolgreich)
                .exec(planBatch())
                .pause(1, 2)
                .doIf(session -> session.contains("batchId")).then(
                        exec(startBatch())
                                .pause(2, 5)
                                .exec(completeBatch())
                )
                .pause(1, 2)
                // Produktionsauftrag anlegen und freigeben
                .exec(createProductionOrder())
                .pause(1, 2)
                .doIf(session -> session.contains("productionOrderId")).then(
                        exec(releaseProductionOrder())
                )
                .pause(1, 2)
                // Nochmal Chargen-Liste prüfen
                .exec(listBatches());
    }

    /**
     * Read-Only-Produktions-Szenario für lesende Benutzer.
     */
    public static ScenarioBuilder productionReadOnly() {
        return scenario("Produktion Read-Only")
                .exec(AuthenticationScenario.login("admin", "admin123"))
                .repeat(15).on(
                        randomSwitch().on(
                                percent(40.0).then(listRecipes()),
                                percent(30.0).then(getRandomRecipe()),
                                percent(30.0).then(listBatches())
                        ).pause(1, 3)
                );
    }
}
