from django.conf import settings
from django.db import models
from django.db.models.signals import post_delete
from django.dispatch import receiver
from pathlib import Path


class ReadingStatus(models.TextChoices):
    WANT_TO_READ = "want_to_read", "Want to Read"
    READING = "reading", "Reading"
    FINISHED = "finished", "Finished"
    DNF = "dnf", "Did Not Finish"


class Book(models.Model):
    title = models.CharField(max_length=512, db_index=True)
    author = models.CharField(max_length=256, blank=True, default="", db_index=True)
    genre = models.CharField(max_length=128, blank=True, default="", db_index=True)
    description = models.TextField(blank=True, default="")
    reading_status = models.CharField(max_length=20, choices=ReadingStatus.choices, default=ReadingStatus.WANT_TO_READ, db_index=True)
    total_pages = models.PositiveIntegerField(default=0)
    cover_image = models.URLField(blank=True, default="")
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "books_book"
        verbose_name = "Book"
        verbose_name_plural = "Books"
        ordering = ["title"]
        indexes = [models.Index(fields=["title", "author", "genre"])]

    def __str__(self):
        return self.title


class EBook(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="ebooks")
    title = models.CharField(max_length=512, db_index=True)
    author = models.CharField(max_length=256, blank=True, default="", db_index=True)
    format = models.CharField(max_length=20, blank=True, default="", editable=False)
    page_count = models.PositiveIntegerField(default=0)
    file_size = models.BigIntegerField(default=0)
    metadata_json = models.JSONField(blank=True, default=dict)
    file = models.FileField(upload_to="ebooks/%Y/%m/%d/")
    cover_image = models.ImageField(upload_to="ebook_covers/%Y/%m/%d/", blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "books_ebook"
        verbose_name = "E-Book"
        verbose_name_plural = "E-Books"
        ordering = ["-created_at"]
        indexes = [models.Index(fields=["user", "-created_at"])]

    def __str__(self):
        return self.title

    def filename(self):
        return Path(self.file.name).name if self.file else ""


class BookChapter(models.Model):
    ebook = models.ForeignKey(EBook, on_delete=models.CASCADE, related_name="chapters")
    title = models.CharField(max_length=512)
    index = models.IntegerField(default=0)
    href = models.CharField(max_length=1024, blank=True, default="")
    children = models.JSONField(blank=True, default=list)

    class Meta:
        db_table = "books_book_chapter"
        verbose_name = "Book Chapter"
        verbose_name_plural = "Book Chapters"
        ordering = ["index"]
        indexes = [models.Index(fields=["ebook", "index"])]

    def __str__(self):
        return f"{self.ebook.title} - {self.title}"


@receiver(post_delete, sender=EBook)
def _auto_delete_ebook_file(sender, instance, **kwargs):
    if instance.file:
        instance.file.delete(save=False)
    if instance.cover_image:
        instance.cover_image.delete(save=False)


class DownloadRecord(models.Model):
    """Tracks book downloads for offline access management."""

    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="download_records")
    ebook = models.ForeignKey(EBook, on_delete=models.CASCADE, related_name="download_records")
    file_size = models.BigIntegerField(default=0)
    downloaded_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "books_download_record"
        verbose_name = "Download Record"
        verbose_name_plural = "Download Records"
        ordering = ["-downloaded_at"]
        unique_together = [("user", "ebook")]

    def __str__(self):
        return f"{self.user} - {self.ebook.title}"


class ReadingProgress(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="reading_progress")
    ebook = models.OneToOneField(EBook, on_delete=models.CASCADE, related_name="reading_progress")
    current_position = models.FloatField(default=0.0)
    last_page = models.IntegerField(default=0)
    epub_location = models.CharField(max_length=2048, blank=True, default="")
    device_id = models.CharField(max_length=128, blank=True, default="")
    device_name = models.CharField(max_length=128, blank=True, default="")
    version = models.PositiveIntegerField(default=1)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        db_table = "books_reading_progress"
        verbose_name_plural = "reading progress"
        unique_together = [("user", "ebook")]

    def __str__(self):
        return f"{self.ebook.title} - {self.current_position:.1f}%"

    def update_with_sync(self, position: float, last_page: int,
                         device_id: str, device_name: str,
                         client_updated_at: str | None = None) -> tuple["ReadingProgress", bool]:
        """Update progress with conflict resolution (last-write-wins by timestamp).

        Returns (instance, applied) where applied is True if the update was applied.
        """
        if client_updated_at and self.updated_at:
            try:
                from django.utils.timezone import is_naive, make_aware
                from datetime import datetime
                client_dt = datetime.fromisoformat(client_updated_at.replace("Z", "+00:00"))
                if is_naive(client_dt):
                    client_dt = make_aware(client_dt)
                if client_dt <= self.updated_at:
                    return self, False
            except (ValueError, TypeError):
                pass

        self.current_position = position
        self.last_page = last_page
        self.device_id = device_id
        self.device_name = device_name
        self.version += 1
        self.save(update_fields=[
            "current_position", "last_page",
            "device_id", "device_name", "version", "updated_at",
        ])
        return self, True