import logging

from datetime import timedelta

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

from books.koreader import SESSION_GAP_SECONDS, fix_long_play_stats_for_scrobbles

logger = logging.getLogger(__name__)
SESSION_GAP = timedelta(seconds=SESSION_GAP_SECONDS)


def _page_data_keys(pages):
    return sorted(int(k) for k in (pages or {}))


class Command(BaseCommand):
    help = "Merge orphaned 1-page KOReader scrobbles into the preceding scrobble"

    def add_arguments(self, parser):
        parser.add_argument(
            "--commit",
            action="store_true",
            help="Commit changes to the database",
        )

    def handle(self, *args, **options):
        from scrobbles.models import Scrobble

        commit = options["commit"]

        qs = Scrobble.objects.filter(
            media_type="Book", source="KOReader"
        ).order_by("book_id", "timestamp")

        if not qs.exists():
            self.stdout.write("No KOReader book scrobbles found.")
            return

        merged = 0
        affected_books = set()

        # Group by book_id manually since we're iterating in order
        book_scrobbles = {}
        for s in qs:
            book_scrobbles.setdefault(s.book_id, []).append(s)

        if not commit:
            self.stdout.write("Dry run — no changes will be saved. Use --commit to apply.")

        for book_id, scrobbles in book_scrobbles.items():
            batch_merged = 0
            i = 0
            while i < len(scrobbles) - 1:
                current = scrobbles[i]
                orphan = scrobbles[i + 1]

                orphan_pages = orphan.logdata.page_data if orphan.logdata else {}
                orphan_keys = _page_data_keys(orphan_pages)
                if len(orphan_keys) != 1:
                    i += 1
                    continue

                current_pages = current.logdata.page_data if current.logdata else {}
                current_keys = _page_data_keys(current_pages)
                if not current_keys:
                    i += 1
                    continue

                orphan_page_num = orphan_keys[0]
                current_last_page = current_keys[-1]

                if orphan_page_num != current_last_page + 1:
                    i += 1
                    continue

                # Check that the orphan is close enough in time
                gap = orphan.timestamp - current.stop_timestamp
                if gap > SESSION_GAP:
                    i += 1
                    continue

                # Merge orphan into current
                current_pages[str(orphan_page_num)] = orphan_pages[str(orphan_page_num)]
                current.log["page_data"] = current_pages
                current.log["pages_read"] = len(current_pages)
                current.stop_timestamp = orphan.stop_timestamp
                current.playback_position_seconds += orphan.playback_position_seconds

                affected_books.add(book_id)

                if commit:
                    with transaction.atomic():
                        current.save(
                            update_fields=[
                                "log",
                                "stop_timestamp",
                                "playback_position_seconds",
                            ]
                        )
                        orphan.delete()

                merged += 1
                batch_merged += 1
                scrobbles.pop(i + 1)

            if batch_merged:
                self.stdout.write(
                    f"  Book {book_id}: merged {batch_merged} orphan scrobble(s)"
                )

        self.stdout.write(f"\nTotal orphans merged: {merged}")

        if commit and affected_books:
            self.stdout.write("Recalculating long_play_stats for affected books...")
            for book_id in affected_books:
                scrobbles_to_fix = (
                    Scrobble.objects.filter(book_id=book_id, source="KOReader")
                    .order_by("timestamp")
                )
                fix_long_play_stats_for_scrobbles(list(scrobbles_to_fix))

            self.stdout.write(f"Fixed stats for {len(affected_books)} books.")

        if not commit:
            self.stdout.write(
                f"\nWould merge {merged} orphan scrobble(s) across "
                f"{len(affected_books)} book(s)."
            )
