"""Independent BSM finite-difference checks. Python 3.10+, no packages.
Without --live, makes no API calls. --live checks one Free calculator response.
Hypothetical European call: S=K=580, DTE=30, sigma=.18, r=.045, q=.013.
"""
import argparse
import json
import math
import os
from urllib.error import HTTPError, URLError
from urllib.parse import urlencode
from urllib.request import Request, urlopen


def get(path, params):
    key = os.environ.get("FLASHALPHA_API_KEY")
    if not key:
        raise RuntimeError("Set FLASHALPHA_API_KEY before using --live.")
    try:
        request = Request("https://lab.flashalpha.com" + path + "?" + urlencode(params),
                          headers={"X-Api-Key": key})
        with urlopen(request, timeout=20) as response:
            return json.load(response)
    except HTTPError as error:
        raise RuntimeError(f"HTTP {error.code}; check key, access and allowance before retrying.") from None
    except (URLError, TimeoutError, json.JSONDecodeError):
        raise RuntimeError("No usable API response; retry later.") from None


def price(spot=580, strike=580, dte=30, sigma=0.18, r=0.045, q=0.013):
    t = dte / 365
    d1 = (math.log(spot / strike) + (r - q + sigma * sigma / 2) * t) / (sigma * math.sqrt(t))
    d2 = d1 - sigma * math.sqrt(t)
    cdf = lambda x: (1 + math.erf(x / math.sqrt(2))) / 2
    return spot * math.exp(-q * t) * cdf(d1) - strike * math.exp(-r * t) * cdf(d2)


def reference():
    h = 0.01
    return {"theoretical_price": price(), "first_order": {
        "delta": (price(spot=580 + h) - price(spot=580 - h)) / (2 * h),
        "gamma": (price(spot=580 + h) - 2 * price() + price(spot=580 - h)) / h ** 2,
        "theta": -(price(dte=30 + h) - price(dte=30 - h)) / (2 * h),
        "vega": (price(sigma=0.18001) - price(sigma=0.17999)) / 0.00002 / 100,
        "rho": (price(r=0.04501) - price(r=0.04499)) / 0.00002}}


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--live", action="store_true")
    args = parser.parse_args()
    expected = reference()
    print("Independent hypothetical BSM reference (not captured API output):")
    print(json.dumps(expected, indent=2))
    if args.live:
        actual = get("/v1/pricing/greeks", {"spot": 580, "strike": 580, "dte": 30,
                                           "sigma": 0.18, "type": "call", "r": 0.045, "q": 0.013})
        if not math.isclose(actual["theoretical_price"], expected["theoretical_price"], abs_tol=0.00002):
            raise RuntimeError("API price differs from the independent reference.")
        for name, value in expected["first_order"].items():
            if not math.isclose(actual["first_order"][name], value, abs_tol=0.00002):
                raise RuntimeError(f"API {name} differs from the independent reference.")
        print("Live calculator matches the reference within 0.00002.")


if __name__ == "__main__":
    main()
