import uuid
from datetime import datetime, timedelta

from django.test import TestCase

from couchforms.models import UnfinishedSubmissionStub

from corehq.form_processor.management.commands.run_submission_reprocessing_queue import (
    get_unfinished_stub_ids_to_process,
)


def _create_stub(timestamp_offset_mins=31, date_queued_offset_hours=None, attempts=0):
    date_queued = None
    if date_queued_offset_hours is not None:
        date_queued = datetime.utcnow() - timedelta(hours=date_queued_offset_hours)
    return UnfinishedSubmissionStub.objects.create(
        xform_id=uuid.uuid4().hex,
        timestamp=datetime.utcnow() - timedelta(minutes=timestamp_offset_mins),
        date_queued=date_queued,
        attempts=attempts
    )


class TestReprocessingQueue(TestCase):
    def tearDown(self):
        UnfinishedSubmissionStub.objects.all().delete()

    def test_get_stubs_early_cutoff(self):
        stubs = [
            _create_stub(29),
            _create_stub(31)
        ]
        ids = get_unfinished_stub_ids_to_process()
        self.assertEqual(ids, [stubs[1].id])

    def test_get_stubs_backoff(self):
        interval = 7
        expected = []
        backoff_factor = 3
        for attempts in (0, 1, 2):
            factor = backoff_factor ** attempts
            # create one stub on either side of the window, only the 2nd stub is expected to be returned
            stubs = [
                _create_stub(date_queued_offset_hours=(interval - 0.1) * factor, attempts=attempts),
                _create_stub(date_queued_offset_hours=(interval + 0.1) * factor, attempts=attempts)
            ]
            expected.append(stubs[1].id)
        ids = get_unfinished_stub_ids_to_process()
        self.assertEqual(ids, expected)

@@ -783,16 +783,65 @@
 tCase):%0A
+    BACKOFF_INTERVAL = 7%0A    BACKOFF_FACTOR = 3%0A%0A
     def 
@@ -1182,29 +1182,8 @@
 f):%0A
-        interval = 7%0A
     
@@ -1204,35 +1204,8 @@
  %5B%5D%0A
-        backoff_factor = 3%0A
     
@@ -1260,22 +1260,27 @@
 r = 
-backoff_factor
+self.BACKOFF_FACTOR
  ** 
@@ -1470,24 +1470,37 @@
 _hours=(
-interval
+self.BACKOFF_INTERVAL
  - 0.1) 
@@ -1588,16 +1588,29 @@
 rs=(
-interval
+self.BACKOFF_INTERVAL
  + 0
@@ -1788,8 +1788,928 @@
 pected)%0A
+%0A    def test_get_stubs_max_attempts(self):%0A        expected = %5B_create_stub(attempts=7).id%5D%0A        _create_stub(attempts=8)%0A        ids = get_unfinished_stub_ids_to_process()%0A        self.assertEqual(ids, expected)%0A%0A    def test_get_stubs_max_backoff(self):%0A        # after 5 attempts the date filter should be constant%0A        offset = (self.BACKOFF_INTERVAL + 0.1) * (self.BACKOFF_FACTOR ** 5)%0A        stubs = %5B%0A            _create_stub(date_queued_offset_hours=offset, attempts=5),%0A            _create_stub(date_queued_offset_hours=offset, attempts=6),%0A            _create_stub(date_queued_offset_hours=offset, attempts=7),%0A        %5D%0A        expected = %5Bstub.id for stub in stubs%5D%0A        ids = get_unfinished_stub_ids_to_process()%0A        self.assertEqual(ids, expected)%0A%0A    def test_large_attempts(self):%0A        _create_stub(date_queued_offset_hours=1, attempts=60)%0A        get_unfinished_stub_ids_to_process()%0A

add tests