#include <warfactory/contracts.hpp>
#include <vector>
#include <memory>
#include <cmath>

// =============================================================================
// EXEMPLE D'UTILISATION DES DÉFENSES WARFACTORY
// =============================================================================

namespace warfactory::examples {

// =============================================================================
// ECONOMIC ENGINE EXAMPLE
// =============================================================================

class Market {
private:
    std::vector<double> prices_;
    double total_volume_ = 0.0;

public:
    // Constructor avec contracts
    explicit Market(size_t initial_capacity) {
        WARFACTORY_REQUIRE(initial_capacity > 0);

        prices_.reserve(initial_capacity);

        WARFACTORY_ENSURE(prices_.capacity() >= initial_capacity);
        WARFACTORY_INVARIANT(total_volume_ == 0.0);
    }

    // Ajouter un prix au marché (peut être négatif !)
    void add_price(double price, double volume) {
        WARFACTORY_REQUIRE_NOT_NULL(&prices_);
        WARFACTORY_REQUIRE(std::isfinite(price));
        WARFACTORY_REQUIRE(std::isfinite(volume));
        WARFACTORY_REQUIRE(volume > 0.0); // Volume doit être positif par contre

        double old_total = total_volume_;

        prices_.push_back(price);
        total_volume_ += volume;

        WARFACTORY_ENSURE(prices_.size() > 0);
        WARFACTORY_ENSURE(std::isfinite(total_volume_));
        WARFACTORY_ENSURE(total_volume_ == old_total + volume);
        WARFACTORY_INVARIANT(check_invariants());
    }

    // Calculer prix moyen pondéré
    double weighted_average_price() const {
        WARFACTORY_REQUIRE(!prices_.empty());
        WARFACTORY_REQUIRE(total_volume_ > 0.0);

        double weighted_sum = 0.0;
        double volume_per_price = total_volume_ / prices_.size();

        for (double price : prices_) {
            WARFACTORY_PROPERTY_FINITE_VALUES(price);
            weighted_sum += price * volume_per_price;
        }

        double result = weighted_sum / total_volume_;

        WARFACTORY_ENSURE_NOT_NULL(&result);
        WARFACTORY_ENSURE(std::isfinite(result));

        return result;
    }

    // Exemple de fail-fast sur condition impossible
    void process_transaction(double amount) {
        WARFACTORY_REQUIRE(std::isfinite(amount));

        if (amount == 0.0) {
            // Transaction vide, juste ignorer
            return;
        }

        // Condition qui ne devrait jamais arriver
        if (std::isnan(amount) || std::isinf(amount)) {
            WARFACTORY_FAIL_FAST_IF(true); // Crash immédiat
        }

        // Traitement normal...
        total_volume_ += std::abs(amount);

        WARFACTORY_INVARIANT(check_invariants());
    }

private:
    bool check_invariants() const {
        return std::isfinite(total_volume_) &&
               total_volume_ >= 0.0 &&
               prices_.size() < 1000000; // Limite raisonnable
    }
};

// =============================================================================
// FACTORY ENGINE EXAMPLE
// =============================================================================

class Production {
private:
    std::vector<double> inputs_;
    std::vector<double> outputs_;

public:
    void add_production_step(const std::vector<double>& inputs,
                           const std::vector<double>& outputs) {
        WARFACTORY_REQUIRE(!inputs.empty());
        WARFACTORY_REQUIRE(!outputs.empty());
        WARFACTORY_REQUIRE(inputs.size() == outputs.size());

        double total_input = 0.0;
        double total_output = 0.0;

        for (size_t i = 0; i < inputs.size(); ++i) {
            WARFACTORY_REQUIRE(std::isfinite(inputs[i]));
            WARFACTORY_REQUIRE(std::isfinite(outputs[i]));
            WARFACTORY_REQUIRE(inputs[i] >= 0.0);  // Les inputs sont non-négatifs
            WARFACTORY_REQUIRE(outputs[i] >= 0.0); // Les outputs aussi

            total_input += inputs[i];
            total_output += outputs[i];

            inputs_.push_back(inputs[i]);
            outputs_.push_back(outputs[i]);
        }

        // Conservation de masse (avec tolérance)
        WARFACTORY_PROPERTY_CONSERVATION_OF_MASS(total_input, total_output);

        WARFACTORY_ENSURE(inputs_.size() == outputs_.size());
        WARFACTORY_INVARIANT(!inputs_.empty() && !outputs_.empty());
    }
};

// =============================================================================
// PROPERTY-BASED TESTING EXAMPLE
// =============================================================================

// Test que toutes les valeurs restent finies
WARFACTORY_PROPERTY(market_values_always_finite, Market& market, double price, double volume) {
    if (volume <= 0.0) return; // Skip invalid volumes
    if (!std::isfinite(price) || !std::isfinite(volume)) return; // Skip invalid inputs

    market.add_price(price, volume);

    // Property: Le prix moyen doit toujours être fini
    if (!market.weighted_average_price()) {
        WARFACTORY_UNREACHABLE(); // Ne devrait jamais arriver
    }

    double avg = market.weighted_average_price();
    WARFACTORY_PROPERTY_FINITE_VALUES(avg);
}

// Test de conservation de masse
WARFACTORY_PROPERTY(production_conserves_mass, Production& prod) {
    std::vector<double> inputs = {10.0, 5.0, 2.0};
    std::vector<double> outputs = {8.0, 6.0, 3.0}; // Total égal

    // Cette propriété devrait passer
    prod.add_production_step(inputs, outputs);
}

} // namespace warfactory::examples

// =============================================================================
// MAIN EXAMPLE
// =============================================================================

int main() {
    try {
        using namespace warfactory::examples;

        // Test du marché avec prix négatifs (subventions)
        Market market(100);

        market.add_price(150.0, 10.0);   // Prix normal
        market.add_price(-50.0, 5.0);    // Subvention !
        market.add_price(200.0, 15.0);   // Prix élevé

        double avg = market.weighted_average_price();
        printf("Prix moyen pondéré: %.2f (avec subventions)\n", avg);

        // Test de production
        Production prod;
        std::vector<double> inputs = {100.0, 50.0};
        std::vector<double> outputs = {75.0, 75.0}; // Conservation parfaite

        prod.add_production_step(inputs, outputs);
        printf("Production ajoutée avec conservation de masse\n");

        return 0;

    } catch (const warfactory::contracts::ContractViolation& e) {
        fprintf(stderr, "CONTRACT VIOLATION: %s\n", e.what());
        return 1;
    } catch (const std::exception& e) {
        fprintf(stderr, "ERROR: %s\n", e.what());
        return 1;
    }
}