"""A website is not re-read on every press the way an API is re-called."""
from datetime import datetime, timedelta, timezone
from unittest.mock import patch

from app.api import endpoints
from app.api.endpoints import CRAWL_MIN_INTERVAL_HOURS, _crawled_recently

TENANT_ID = "org_x"


def _source(kind="crawl", hours_ago=None, config=None):
    return {
        "kind": kind,
        "external_ref": "shop.example.com",
        "config": config if config is not None else {},
        "last_synced_at": (None if hours_ago is None else
                           datetime.now(timezone.utc) - timedelta(hours=hours_ago)),
    }


def _crawled_recently_with_products(source, has_products=True):
    """has_any_products only runs a real DB query when the timestamp check
    alone hasn't already decided the answer -- mocked here so these tests
    don't need a database."""
    with patch.object(endpoints, "has_any_products", return_value=has_products):
        return _crawled_recently(source, TENANT_ID)


def test_a_website_read_an_hour_ago_is_skipped():
    assert _crawled_recently_with_products(_source(hours_ago=1)) is True


def test_a_website_read_before_the_window_is_read_again():
    assert _crawled_recently_with_products(
        _source(hours_ago=CRAWL_MIN_INTERVAL_HOURS + 1)) is False


def test_a_website_never_read_is_always_read():
    assert _crawled_recently_with_products(_source(hours_ago=None)) is False


def test_only_websites_are_throttled():
    # An API call is one request; a crawl is dozens against the merchant's own
    # server. Throttling Shopify would just make the catalogue stale.
    assert _crawled_recently_with_products(_source(kind="shopify", hours_ago=1)) is False
    assert _crawled_recently_with_products(_source(kind="http_api", hours_ago=1)) is False


def test_a_merchant_can_opt_out_of_the_throttle():
    opted_out = _source(hours_ago=1, config={"min_interval_hours": 0})
    assert _crawled_recently_with_products(opted_out) is False


def test_the_window_can_be_shortened_per_source():
    source = _source(hours_ago=2, config={"min_interval_hours": 1})
    assert _crawled_recently_with_products(source) is False


def test_a_recently_synced_source_with_no_products_is_not_skipped():
    # last_synced_at only says the source was crawled recently -- not that
    # the products it wrote still exist. A cleanup that deletes product rows
    # without resetting this timestamp must not leave a build that reports
    # success while serving an empty catalogue.
    source = _source(hours_ago=1)
    assert _crawled_recently_with_products(source, has_products=False) is False


def test_has_any_products_is_only_checked_once_the_timestamp_says_skip():
    # A never-synced or stale source shouldn't need a DB round trip at all.
    with patch.object(endpoints, "has_any_products") as mock_has_products:
        _crawled_recently(_source(hours_ago=None), TENANT_ID)
        _crawled_recently(_source(hours_ago=CRAWL_MIN_INTERVAL_HOURS + 1), TENANT_ID)
    mock_has_products.assert_not_called()
