from __future__ import annotations

from typing import TYPE_CHECKING

from django.db import models

from accounts.models import Workspace
from members.models import Member
from sources.models import Source

if TYPE_CHECKING:
    from integrations.contracts import GitHubSignalMeta, GitHubThreadMeta


class TimestampedModel(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True


class Tag(TimestampedModel):
    workspace = models.ForeignKey(Workspace, on_delete=models.CASCADE, related_name="tags")
    name = models.CharField(max_length=100)

    class Meta:
        unique_together = [("workspace", "name")]

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


class Thread(TimestampedModel):
    STATUS_CHOICES = [
        ("open", "Open"),
        ("triaged", "Triaged"),
        ("closed", "Closed"),
    ]

    THREAD_TYPE_CHOICES = [
        ("conversation", "Conversation"),
        ("post", "Post"),
    ]

    workspace = models.ForeignKey(Workspace, on_delete=models.CASCADE, related_name="threads")
    source = models.ForeignKey(Source, on_delete=models.CASCADE, related_name="threads")
    external_id = models.CharField(max_length=255)
    title = models.CharField(max_length=255)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="open")
    thread_type = models.CharField(
        max_length=20,
        choices=THREAD_TYPE_CHOICES,
        default="conversation",
        db_index=True,
    )
    tags = models.ManyToManyField(Tag, related_name="threads", blank=True)

    # Extensible metadata for integration-specific data (GitHub, etc.)
    metadata = models.JSONField(default=dict, blank=True)

    # Timestamp for feed ordering (updated when new comments are synced)
    activity_at = models.DateTimeField(null=True, blank=True, db_index=True)

    class Meta:
        unique_together = [("source", "external_id")]
        indexes = [
            models.Index(fields=["workspace", "-activity_at"]),
            models.Index(fields=["workspace", "thread_type", "-activity_at"]),
        ]

    def __str__(self) -> str:
        return self.title

    @property
    def github(self) -> GitHubThreadMeta | None:
        """Type-safe accessor for GitHub metadata.

        Returns the GitHub-specific metadata if this thread is from a GitHub source,
        or None if not present.

        Usage:
            if thread.github:
                print(thread.github["number"])  # Type checker knows the structure
        """
        github_meta = self.metadata.get("github")
        if github_meta is None:
            return None
        return github_meta  # type: ignore[no-any-return]


class Signal(TimestampedModel):
    """Container for events - standalone occurrences without reply structure.

    Use Signal for: mentions, stars, forks, releases, follows - events that don't
    have a conversation/reply structure.
    """

    SIGNAL_TYPE_CHOICES = [
        ("mention", "Mention"),
        ("star", "Star"),
        ("fork", "Fork"),
        ("release", "Release"),
        ("follow", "Follow"),
    ]

    workspace = models.ForeignKey(Workspace, on_delete=models.CASCADE, related_name="signals")
    source = models.ForeignKey(Source, on_delete=models.CASCADE, related_name="signals")
    signal_type = models.CharField(max_length=20, choices=SIGNAL_TYPE_CHOICES, db_index=True)
    occurred_at = models.DateTimeField(db_index=True)
    title = models.CharField(max_length=255)
    external_url = models.URLField(max_length=500)
    external_id = models.CharField(max_length=255, blank=True)
    body = models.TextField(blank=True)
    actor = models.ForeignKey(Member, on_delete=models.SET_NULL, null=True, blank=True, related_name="signals")

    # Extensible metadata for integration-specific data
    metadata = models.JSONField(default=dict, blank=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["source", "external_id"],
                name="unique_signal_source_external_id",
                condition=models.Q(external_id__gt=""),
            ),
        ]
        indexes = [
            models.Index(fields=["workspace", "-occurred_at"]),
            models.Index(fields=["workspace", "signal_type", "-occurred_at"]),
        ]

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

    @property
    def github(self) -> GitHubSignalMeta | None:
        """Type-safe accessor for GitHub metadata.

        Returns the GitHub-specific metadata if this signal is from a GitHub source,
        or None if not present.

        Usage:
            if signal.github:
                print(signal.github["starred_at"])  # Type checker knows the structure
        """
        github_meta = self.metadata.get("github")
        if github_meta is None:
            return None
        return github_meta  # type: ignore[no-any-return]


class Message(TimestampedModel):
    thread = models.ForeignKey(Thread, on_delete=models.CASCADE, related_name="messages")
    author = models.ForeignKey(Member, on_delete=models.SET_NULL, related_name="messages", null=True, blank=True)
    external_id = models.CharField(max_length=255, blank=True)
    content = models.TextField()
    sent_at = models.DateTimeField()

    # Extensible metadata for integration-specific data (GitHub comments, etc.)
    metadata = models.JSONField(default=dict, blank=True)

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