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.*;

/**
 * Stammdaten-Szenario: Artikel, Lieferanten, Kunden – 80% Read, 20% Write.
 */
public final class MasterDataScenario {

    private MasterDataScenario() {}

    // ---- Artikel ----

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

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

    public static ChainBuilder createArticle() {
        return exec(session -> {
            var catIds = LoadTestDataSeeder.categoryIds();
            String catId = catIds.get(ThreadLocalRandom.current().nextInt(catIds.size()));
            return session.set("newArticleBody", JsonBodyBuilder.createArticleBody(catId));
        }).exec(
                http("Artikel anlegen")
                        .post("/api/articles")
                        .header("Authorization", "Bearer #{accessToken}")
                        .header("Content-Type", "application/json")
                        .body(StringBody("#{newArticleBody}"))
                        .check(status().in(200, 201, 400, 409, 500))
                        .check(jsonPath("$.id").optional().saveAs("createdArticleId"))
        );
    }

    public static ChainBuilder updateArticle() {
        return exec(session -> session.set("updateArticleBody", JsonBodyBuilder.updateArticleBody()))
                .exec(
                        http("Artikel bearbeiten")
                                .put("/api/articles/#{createdArticleId}")
                                .header("Authorization", "Bearer #{accessToken}")
                                .header("Content-Type", "application/json")
                                .body(StringBody("#{updateArticleBody}"))
                                .check(status().is(200))
                );
    }

    // ---- Lieferanten ----

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

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

    public static ChainBuilder createSupplier() {
        return exec(session -> session.set("newSupplierBody", JsonBodyBuilder.createSupplierBody()))
                .exec(
                        http("Lieferant anlegen")
                                .post("/api/suppliers")
                                .header("Authorization", "Bearer #{accessToken}")
                                .header("Content-Type", "application/json")
                                .body(StringBody("#{newSupplierBody}"))
                                .check(status().in(200, 201, 400, 409, 500))
                );
    }

    // ---- Kunden ----

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

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

    public static ChainBuilder createCustomer() {
        return exec(session -> session.set("newCustomerBody", JsonBodyBuilder.createCustomerBody()))
                .exec(
                        http("Kunde anlegen")
                                .post("/api/customers")
                                .header("Authorization", "Bearer #{accessToken}")
                                .header("Content-Type", "application/json")
                                .body(StringBody("#{newCustomerBody}"))
                                .check(status().in(200, 201, 400, 409, 500))
                );
    }

    // ---- Kategorien ----

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

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

    /**
     * Stammdaten-CRUD-Workflow mit 80/20 Read/Write-Verteilung.
     */
    public static ScenarioBuilder masterDataWorkflow() {
        return scenario("Stammdaten CRUD")
                .exec(AuthenticationScenario.login("admin", "admin123"))
                .repeat(15).on(
                        randomSwitch().on(
                                percent(20.0).then(listArticles()),
                                percent(15.0).then(getRandomArticle()),
                                percent(10.0).then(listSuppliers()),
                                percent(10.0).then(getRandomSupplier()),
                                percent(10.0).then(listCustomers()),
                                percent(10.0).then(getRandomCustomer()),
                                percent(5.0).then(listCategories()),
                                percent(5.0).then(createArticle()),
                                percent(5.0).then(createSupplier()),
                                percent(5.0).then(createCustomer()),
                                percent(5.0).then(
                                        createArticle()
                                                .pause(1)
                                                .doIf(session -> session.contains("createdArticleId")).then(
                                                        exec(updateArticle())
                                                )
                                )
                        ).pause(1, 3)
                );
    }

    /**
     * Read-Only-Stammdaten-Szenario für zusätzliche Leselast.
     */
    public static ScenarioBuilder masterDataReadOnly() {
        return scenario("Stammdaten Read-Only")
                .exec(AuthenticationScenario.login("admin", "admin123"))
                .repeat(15).on(
                        randomSwitch().on(
                                percent(25.0).then(listArticles()),
                                percent(20.0).then(getRandomArticle()),
                                percent(15.0).then(listSuppliers()),
                                percent(15.0).then(getRandomSupplier()),
                                percent(10.0).then(listCustomers()),
                                percent(10.0).then(getRandomCustomer()),
                                percent(5.0).then(listCategories())
                        ).pause(1, 3)
                );
    }
}
