#include <finance.hpp>
#include <cmath>
#include <exception>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <random>
#include <stdexcept>
#include <string>
#include <vector>

int main(int argc, char** argv) {
    try {
        if (argc != 2) {
            throw std::invalid_argument("Usage: finance-confidence-bands output.svg");
        }
        std::mt19937 random(42);
        std::normal_distribution<double> normal;
        std::vector<std::vector<double>> paths;
        for (int i = 0; i < 20000; ++i) paths.push_back({normal(random)});
        std::ofstream svg(argv[1]);
        svg.exceptions(std::ios::failbit | std::ios::badbit);
        svg << std::fixed << std::setprecision(4)
            << "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 960 560\" "
               "role=\"img\" aria-labelledby=\"title description\">"
               "<title id=\"title\">CoolBox option pricing with Monte Carlo confidence bands</title>"
               "<desc id=\"description\">Two price-versus-spot charts compare 2,000 and 20,000 "
               "independent paths. Blue bands are pointwise approximate 95 percent confidence "
               "intervals for the estimated call price. Dashed orange is Black-Scholes.</desc>"
               "<rect width=\"960\" height=\"560\" fill=\"white\"/>"
               "<g font-family=\"Arial, sans-serif\" fill=\"#596579\">"
               "<text x=\"54\" y=\"42\" font-size=\"22\">Option pricing: uncertainty bands</text>"
               "<text x=\"54\" y=\"69\" font-size=\"13\">Strike 100 | rate 5% | volatility 20% | maturity 1 year | seed 42</text>";
        for (int panel = 0; panel < 2; ++panel) {
            const int count = panel == 0 ? 2000 : 20000;
            const double left = panel == 0 ? 90 : 550;
            const std::vector<std::vector<double>> shocks(paths.begin(), paths.begin() + count);
            std::vector<finance::MonteCarloEstimate> estimates;
            std::vector<double> closed;
            for (int i = 0; i <= 40; ++i) {
                const double spot = 80 + i;
                estimates.push_back(finance::monte_carlo_european_call_estimate(
                    spot, 100, 0.05, 0.20, {1.0}, shocks));
                closed.push_back(finance::black_scholes_call_price(spot, 100, 0.05, 0.20, 1));
            }
            auto x = [&](int i) { return left + i * 7.5; };
            auto y = [](double price) { return 420 - price * 10; };
            svg << "<text x=\"" << left << "\" y=\"112\" font-size=\"17\">"
                << (panel == 0 ? "2,000" : "20,000") << " independent paths</text>";
            for (int tick = 0; tick <= 25; tick += 5) {
                svg << "<path d=\"M" << left << ' ' << y(tick) << "h300\" "
                       "stroke=\"#e9edf3\" fill=\"none\"/>"
                    << "<text x=\"" << left - 12 << "\" y=\"" << y(tick) + 4
                    << "\" text-anchor=\"end\" font-size=\"12\">" << tick << "</text>";
            }
            svg << "<polygon fill=\"#325adc\" fill-opacity=\"0.20\" points=\"";
            for (int i = 0; i <= 40; ++i) {
                svg << x(i) << ',' << y(estimates[i].confidence_interval_high) << ' ';
            }
            for (int i = 40; i >= 0; --i) {
                svg << x(i) << ',' << y(estimates[i].confidence_interval_low) << ' ';
            }
            svg << "\"/>";
            for (int line = 0; line < 2; ++line) {
                svg << "<polyline fill=\"none\" stroke=\""
                    << (line == 0 ? "#325adc" : "#a45b29") << "\" stroke-width=\"2\" "
                    << (line == 1 ? "stroke-dasharray=\"6 4\" " : "") << "points=\"";
                for (int i = 0; i <= 40; ++i) {
                    svg << x(i) << ',' << y(line == 0 ? estimates[i].price : closed[i]) << ' ';
                }
                svg << "\"/>";
            }
            svg << "<path d=\"M" << left << " 150V420h300\" fill=\"none\" stroke=\"#9aa5b5\"/>";
            for (int i = 0; i <= 40; i += 10) {
                svg << "<text x=\"" << x(i) << "\" y=\"443\" text-anchor=\"middle\" "
                       "font-size=\"12\">" << 80 + i << "</text>";
            }
            svg << "<text x=\"" << left + 150
                << "\" y=\"468\" text-anchor=\"middle\" font-size=\"13\">Spot price</text>"
                << "<text x=\"" << left - 49 << "\" y=\"285\" text-anchor=\"middle\" "
                   "font-size=\"13\" transform=\"rotate(-90 " << left - 49
                << " 285)\">Call price</text>";
            const auto& at_money = estimates[20];
            if (!(at_money.standard_error > 0
                  && at_money.confidence_interval_low < at_money.price
                  && at_money.price < at_money.confidence_interval_high)) {
                throw std::runtime_error("Invalid confidence interval");
            }
            std::cout << count << " paths: price " << at_money.price << ", SE "
                      << at_money.standard_error << ", interval ["
                      << at_money.confidence_interval_low << ", "
                      << at_money.confidence_interval_high << "]\n";
        }
        svg << "<text x=\"54\" y=\"507\" font-size=\"12\">Blue: estimate + approximate 95% band | Dashed orange: Black-Scholes reference</text>"
               "<text x=\"54\" y=\"536\" font-size=\"11\">Pointwise sampling uncertainty, not future-price bands or a simultaneous confidence region.</text>"
               "</g></svg>";
        svg.close();
        return 0;
    } catch (const std::exception& error) {
        std::cerr << error.what() << '\n';
        return 1;
    }
}
