from __future__ import annotations

import base64
from typing import Any

from django.db import models

from django_anyllm.settings import get_setting


def _default_model():
    return get_setting("DEFAULT_MODEL")


def _default_provider():
    return get_setting("DEFAULT_PROVIDER")


class AbstractChat(models.Model):
    """Abstract base for a chat conversation."""

    title = models.CharField(max_length=255, blank=True)
    model = models.CharField(max_length=100, default=_default_model)
    provider = models.CharField(max_length=100, blank=True, default=_default_provider)
    system_prompt = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True
        ordering = ["-updated_at"]

    def __str__(self) -> str:
        return self.title or f"Chat {self.pk}"


class AbstractMessage(models.Model):
    """Abstract base for a chat message."""

    class Role(models.TextChoices):
        SYSTEM = "system", "System"
        USER = "user", "User"
        ASSISTANT = "assistant", "Assistant"
        TOOL = "tool", "Tool"

    role = models.CharField(max_length=20, choices=Role.choices)
    content = models.TextField(blank=True)
    tool_calls = models.JSONField(null=True, blank=True)
    tool_results = models.JSONField(null=True, blank=True)
    reasoning = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        abstract = True
        ordering = ["created_at"]

    def __str__(self) -> str:
        return f"{self.role}: {self.content[:50]}"


class AbstractMessageFile(models.Model):
    """Abstract base for a file attached to a message."""

    file = models.FileField(upload_to="anyllm/files/%Y/%m/%d/")
    mime_type = models.CharField(max_length=100, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        abstract = True

    def __str__(self) -> str:
        return self.file.name

    def to_image_content(self) -> dict[str, str] | None:
        """Return an OpenAI-style image_url content part for image files."""
        if not self.mime_type.startswith("image/"):
            return None
        data = self.file.read()
        b64 = base64.b64encode(data).decode()
        return {
            "type": "image_url",
            "image_url": {"url": f"data:{self.mime_type};base64,{b64}"},
        }

    def to_file_content(self) -> dict[str, Any] | None:
        """Return an any-llm-sdk normalized file content part for non-image files."""
        if self.mime_type.startswith("image/"):
            return None
        data = self.file.read()
        b64 = base64.b64encode(data).decode()
        return {
            "type": "file",
            "file": {
                "file_data": f"data:{self.mime_type};base64,{b64}",
                "filename": self.file.name,
            },
        }


# ---------------------------------------------------------------------------
# Concrete defaults
# ---------------------------------------------------------------------------


class Chat(AbstractChat):
    pass


class Message(AbstractMessage):
    chat = models.ForeignKey(
        Chat,
        on_delete=models.CASCADE,
        related_name="messages",
    )


class MessageFile(AbstractMessageFile):
    message = models.ForeignKey(
        Message,
        on_delete=models.CASCADE,
        related_name="files",
    )
