"""

Populate api_check.n_pings and api_ping.n fields.

 - api_ping.n stores ping's serial number, counted separately for
 each check. For example, if a particular check has received 100 pings,
 its first ping will have a n=1, and the 100th ping will have a n=100.

 - api_check.n_pings stores the last serial number assigned to a ping.
 It also is the total number of pings the check has ever received.

This command works by "replaying" stored pings in their primary
key order, and counting up their serial numbers. At the very end,
api_check.n_pings fields are updated as well.

Depending on the size of api_ping table, this command can potentially
take a long time to complete.

Note on ping pruning: when the prunepings command is run, some of the
pings with the lowest serial numbers get removed. This doesn't affect
the "n" field for remaining pings, or the "n_pings" value of checks.
The serial numbers keep going up.

"""

import gc
from collections import Counter

from django.core.management.base import BaseCommand
from django.db import connection, transaction

from hc.api.models import Check, Ping


class Command(BaseCommand):
    help = 'Fill check.n_pings field and ping.n field'

    def handle(self, *args, **options):
        connection.use_debug_cursor = False
        chunksize = 2000

        # Reset all n_pings fields to zero
        Check.objects.update(n_pings=0)

        counts = Counter()

        pk = 0
        if Ping.objects.count() == 0:
            last_pk = 0
        else:
            last_pk = Ping.objects.order_by('-pk')[0].pk
        queryset = Ping.objects.order_by('pk')

        while pk < last_pk:
            transaction.set_autocommit(False)
            while pk < last_pk:
                for ping in queryset.filter(pk__gt=pk)[:chunksize]:
                    pk = ping.pk
                    counts[ping.owner_id] += 1

                    ping.n = counts[ping.owner_id]
                    ping.save(update_fields=("n", ))

                gc.collect()
                progress = 100 * pk / last_pk
                self.stdout.write(
                    "Processed ping id %d (%.2f%%)" % (pk, progress))

                transaction.commit()

            transaction.set_autocommit(True)
            # last_pk might have increased because of influx of new pings:
            last_pk = Ping.objects.order_by('-pk')[0].pk

        self.stdout.write("Updating check.n_pings")
        for check_id, n_pings in counts.items():
            Check.objects.filter(pk=check_id).update(n_pings=n_pings)

        return "Done!"
