from __future__ import annotations

import math

import pytest

from services.pricing import (
    PricingInput,
    PricingMetadata,
    PricingResult,
    calculate_pricing,
)


def test_calculate_pricing_with_explicit_penalties() -> None:
    pricing_input = PricingInput(
        metal="copper",
        ore_tonnage=100_000,
        head_grade_pct=1.2,
        recovery_pct=90,
        payable_pct=96,
        reference_price=8_500,
        treatment_charge=100_000,
        smelting_charge=0,
        moisture_pct=10,
        moisture_threshold_pct=8,
        moisture_penalty_per_pct=3_000,
        impurity_ppm={"As": 100},
        impurity_thresholds={"As": 0},
        impurity_penalty_per_ppm={"As": 2},
        premiums=50_000,
        fx_rate=1.0,
        currency_code="usd",
    )

    result = calculate_pricing(pricing_input)

    assert isinstance(result, PricingResult)
    assert math.isclose(result.payable_metal_tonnes, 1_036.8, rel_tol=1e-6)
    assert math.isclose(result.gross_revenue, 1_036.8 * 8_500, rel_tol=1e-6)
    assert math.isclose(result.moisture_penalty, 6_000, rel_tol=1e-6)
    assert math.isclose(result.impurity_penalty, 200, rel_tol=1e-6)
    assert math.isclose(result.net_revenue, 8_756_600, rel_tol=1e-6)
    assert result.treatment_smelt_charges == pytest.approx(100_000)
    assert result.currency == "USD"


def test_calculate_pricing_with_metadata_defaults() -> None:
    metadata = PricingMetadata(
        default_payable_pct=95,
        default_currency="EUR",
        moisture_threshold_pct=7,
        moisture_penalty_per_pct=2_000,
        impurity_thresholds={"Pb": 50},
        impurity_penalty_per_ppm={"Pb": 1.5},
    )

    pricing_input = PricingInput(
        metal="lead",
        ore_tonnage=50_000,
        head_grade_pct=5,
        recovery_pct=85,
        payable_pct=None,
        reference_price=2_000,
        treatment_charge=30_000,
        smelting_charge=20_000,
        moisture_pct=9,
        moisture_threshold_pct=None,
        moisture_penalty_per_pct=None,
        impurity_ppm={"Pb": 120},
        premiums=12_000,
        fx_rate=1.2,
        currency_code=None,
    )

    result = calculate_pricing(pricing_input, metadata=metadata)

    expected_payable = 50_000 * 0.05 * 0.85 * 0.95
    assert math.isclose(result.payable_metal_tonnes,
                        expected_payable, rel_tol=1e-6)
    assert result.moisture_penalty == pytest.approx((9 - 7) * 2_000)
    assert result.impurity_penalty == pytest.approx((120 - 50) * 1.5)
    assert result.treatment_smelt_charges == pytest.approx(50_000)
    assert result.currency == "EUR"
    assert result.net_revenue > 0


def test_calculate_pricing_currency_override() -> None:
    pricing_input = PricingInput(
        metal="gold",
        ore_tonnage=10_000,
        head_grade_pct=2.5,
        recovery_pct=92,
        payable_pct=98,
        reference_price=60_000,
        treatment_charge=40_000,
        smelting_charge=10_000,
        moisture_pct=5,
        moisture_threshold_pct=7,
        moisture_penalty_per_pct=1_000,
        premiums=25_000,
        fx_rate=1.0,
        currency_code="cad",
    )

    metadata = PricingMetadata(default_currency="USD")

    result = calculate_pricing(
        pricing_input, metadata=metadata, currency="CAD")

    assert result.currency == "CAD"
    assert result.net_revenue > 0


def test_calculate_pricing_multiple_inputs_aggregate() -> None:
    metadata = PricingMetadata(default_currency="USD")
    inputs = [
        PricingInput(
            metal="copper",
            ore_tonnage=10_000,
            head_grade_pct=1.5,
            recovery_pct=88,
            payable_pct=95,
            reference_price=8_000,
            treatment_charge=20_000,
            smelting_charge=5_000,
            moisture_pct=7,
            moisture_threshold_pct=8,
            moisture_penalty_per_pct=1_000,
            premiums=0,
            fx_rate=1.0,
            currency_code=None,
        ),
        PricingInput(
            metal="copper",
            ore_tonnage=8_000,
            head_grade_pct=1.1,
            recovery_pct=90,
            payable_pct=96,
            reference_price=8_000,
            treatment_charge=18_000,
            smelting_charge=4_000,
            moisture_pct=9,
            moisture_threshold_pct=8,
            moisture_penalty_per_pct=1_000,
            premiums=0,
            fx_rate=1.0,
            currency_code="usd",
        ),
    ]

    results = [calculate_pricing(i, metadata=metadata) for i in inputs]

    assert all(result.currency == "USD" for result in results)
    assert sum(result.net_revenue for result in results) > 0
