"""
Presupuesto de cuota: el principio II, verificado.

Tres cosas tienen que ser ciertas y las tres se prueban acá: que no se conceda
más de lo que hay, que el trabajo automático no toque la reserva del manual, y
que lo reservado y no usado vuelva al cupo. Sin la tercera, un fallo temprano en
un lote deja al usuario sin cuota por el resto del día sin que Google le haya
descontado nada.
"""

import pytest

from apps.jobs.budget import Origin, budget_for, release, remaining, reserve
from tests.factories import create_account, create_domain

pytestmark = pytest.mark.django_db


@pytest.fixture
def domain():
    return create_domain(create_account(), daily_inspection_budget=1000, manual_reserve=100)


def test_the_first_reservation_creates_the_budget_of_the_day(domain):
    reservation = reserve(domain, 10)

    budget = budget_for(domain)
    assert reservation.granted == 10
    assert budget.limit_total == 1000
    assert budget.manual_reserve == 100
    assert budget.used_automatic == 10


def test_the_quota_is_not_exceeded(domain):
    """Pedir de más concede lo que queda, no lo que se pidió."""
    reserve(domain, 800)
    reservation = reserve(domain, 500)

    # El automático dispone de 1000 - 100 de reserva manual = 900.
    assert reservation.granted == 100
    assert reservation.is_partial
    assert budget_for(domain).used_automatic == 900


def test_automatic_work_does_not_consume_the_manual_reserve(domain):
    reservation = reserve(domain, 5000)

    assert reservation.granted == 900
    assert budget_for(domain).automatic_remaining == 0
    # Lo reservado para el uso a mano sigue entero.
    assert budget_for(domain).manual_remaining == 100


def test_manual_work_can_use_what_the_automatic_did_not_spend(domain):
    """
    La reserva le garantiza un piso al manual, no le pone un techo.

    Si el automático gastó poco, no tiene sentido negarle al usuario el resto
    del cupo del día: es suyo.
    """
    reserve(domain, 100)
    reservation = reserve(domain, 900, origin=Origin.MANUAL)

    assert reservation.granted == 900


def test_an_unused_reservation_is_released(domain):
    reservation = reserve(domain, 500)
    assert budget_for(domain).used_automatic == 500

    # Se gastaron tres y el lote se cortó: las 497 restantes vuelven.
    release(reservation, 497)

    assert budget_for(domain).used_automatic == 3
    assert budget_for(domain).automatic_remaining == 897


def test_releasing_never_leaves_the_counter_negative(domain):
    reservation = reserve(domain, 10)
    release(reservation, 999)

    assert budget_for(domain).used_automatic == 0


def test_the_reported_balance_matches_what_was_reserved(domain):
    reserve(domain, 200)
    reserve(domain, 50, origin=Origin.MANUAL)

    balance = remaining(domain)

    assert balance['used_automatic'] == 200
    assert balance['used_manual'] == 50
    assert balance['automatic_remaining'] == 700
    assert balance['manual_remaining'] == 750


def test_once_the_quota_is_exhausted_the_reservation_grants_nothing(domain):
    reserve(domain, 900)
    reservation = reserve(domain, 1)

    assert reservation.granted == 0
    # Una reserva sin cupo es falsa: el cliente de Google la rechaza por eso.
    assert not reservation
