import cProfile
import functools
import os
from pathlib import Path
from typing import Callable

import typer

from measured import One, Quantity, _div
from measured.astronomical import JulianYear
from measured.si import Ampere, Meter, Ohm, Second, Volt
from measured.us import Ounce, Ton

app = typer.Typer()
options = {"baseline": False}

profiles = Path(".profiles")


@app.callback()
def main(baseline: bool = False) -> None:
    options["baseline"] = baseline


def profiled(func: Callable[[], None]) -> Callable[[], None]:
    @functools.wraps(func)
    def inner() -> None:
        os.makedirs(profiles, exist_ok=True)
        filename = f"{'baseline-' if options['baseline'] else ''}{func.__name__}.prof"
        cProfile.runctx("func()", {"func": func}, {}, str(profiles / filename))

    return inner


@app.command()
@profiled
def quantity_construction() -> None:
    """Tests for constructing Quantities"""
    for a in range(1, 1000001):
        Quantity(a, Meter)


@app.command()
@profiled
def low_equality() -> None:
    """Tests for equality for low-dimensional quantities"""
    for a in (Quantity(a, Meter) for a in range(1, 1001)):
        for b in (Quantity(b, Meter) for b in range(1, 1001)):
            bool(a == b)


@app.command()
@profiled
def high_equality() -> None:
    """Tests for equality for high-dimensional quantities"""
    for a in (Quantity(a, Ohm) for a in range(1, 1001)):
        for b in (Quantity(b, Ohm) for b in range(1, 1001)):
            bool(a == b)


@app.command()
@profiled
def resistances() -> None:
    """Computes a whole lot of Ohms"""
    for a in (Quantity(a, Ampere) for a in range(1, 1001)):
        for v in (Quantity(v, Volt) for v in range(1, 1001)):
            assert v / a == _div(v.magnitude, a.magnitude) * Ohm


@app.command()
@profiled
def conversions() -> None:
    for o in (Quantity(o, Ounce) for o in range(1, 1001)):
        for t in (Quantity(t, Ton) for t in range(1, 1001)):
            divided = (t / o).in_unit(One)
            assert divided.unit == One
            assert round(divided.magnitude) == round(
                _div(t.magnitude, o.magnitude) * 20 * 100 * 16
            )


@app.command()
@profiled
def complex_conversions() -> None:
    for o in (Quantity(o, Ounce / (JulianYear * Ampere)) for o in range(1, 1001)):
        for t in (Quantity(t, Ton / (Second * Ampere)) for t in range(1, 1001)):
            divided = (t / o).in_unit(One)
            assert divided.unit == One


@app.command()
def list_of_quantities() -> None:
    all_the_ohms = []
    for a in (Quantity(a, Ampere) for a in range(1, 10001)):
        for v in (Quantity(v, Volt) for v in range(1, 1001)):
            all_the_ohms.append(v / a)


if __name__ == "__main__":
    app()
