"""Tests for metadata contracts and typed accessors.

These tests verify that:
1. TypedDict contracts are correctly defined
2. Typed accessors on models work correctly
3. Type checker would catch invalid metadata (demonstrated through type annotations)
"""

import pytest

from integrations.contracts import (
    GitHubDiscussionMeta,
    GitHubForkMeta,
    GitHubIssueMeta,
    GitHubPRMeta,
    GitHubReleaseMeta,
    GitHubSignalMeta,
    GitHubStarMeta,
    GitHubThreadMeta,
)
from messages.contracts import GitHubAuthorMeta, GitHubLabelMeta

from .factories import SignalFactory, ThreadFactory


class TestGitHubAuthorMeta:
    """Tests for GitHubAuthorMeta contract."""

    def test_valid_author_meta(self):
        """Test creating a valid author metadata dict."""
        author: GitHubAuthorMeta = {
            "login": "octocat",
            "id": 123456,
            "avatar_url": "https://avatars.githubusercontent.com/u/123456",
        }
        assert author["login"] == "octocat"
        assert author["id"] == 123456

    def test_author_meta_without_optional_fields(self):
        """Test author metadata without optional avatar_url."""
        author: GitHubAuthorMeta = {
            "login": "octocat",
            "id": 123456,
        }
        assert author["login"] == "octocat"
        assert "avatar_url" not in author


class TestGitHubLabelMeta:
    """Tests for GitHubLabelMeta contract."""

    def test_valid_label_meta(self):
        """Test creating a valid label metadata dict."""
        label: GitHubLabelMeta = {
            "name": "bug",
            "color": "d73a4a",
        }
        assert label["name"] == "bug"
        assert label["color"] == "d73a4a"


class TestGitHubIssueMeta:
    """Tests for GitHubIssueMeta contract."""

    def test_valid_issue_meta(self):
        """Test creating a valid issue metadata dict."""
        issue: GitHubIssueMeta = {
            "type": "issue",
            "number": 42,
            "state": "open",
            "html_url": "https://github.com/org/repo/issues/42",
            "labels": [{"name": "bug", "color": "d73a4a"}],
            "author": {"login": "octocat", "id": 123456},
        }
        assert issue["type"] == "issue"
        assert issue["number"] == 42


class TestGitHubPRMeta:
    """Tests for GitHubPRMeta contract."""

    def test_valid_pr_meta(self):
        """Test creating a valid PR metadata dict."""
        pr: GitHubPRMeta = {
            "type": "pull_request",
            "number": 100,
            "state": "open",
            "html_url": "https://github.com/org/repo/pull/100",
            "labels": [],
            "author": {"login": "contributor", "id": 789},
            "draft": False,
            "merged": False,
            "head_ref": "feature-branch",
            "base_ref": "main",
        }
        assert pr["type"] == "pull_request"
        assert pr["draft"] is False

    def test_pr_meta_without_optional_fields(self):
        """Test PR metadata without optional ref fields."""
        pr: GitHubPRMeta = {
            "type": "pull_request",
            "number": 100,
            "state": "open",
            "html_url": "https://github.com/org/repo/pull/100",
            "labels": [],
            "author": {"login": "contributor", "id": 789},
            "draft": False,
            "merged": False,
        }
        assert "head_ref" not in pr


class TestGitHubDiscussionMeta:
    """Tests for GitHubDiscussionMeta contract."""

    def test_valid_discussion_meta(self):
        """Test creating a valid discussion metadata dict."""
        discussion: GitHubDiscussionMeta = {
            "type": "discussion",
            "number": 5,
            "state": "open",
            "html_url": "https://github.com/org/repo/discussions/5",
            "labels": [],
            "author": {"login": "user", "id": 111},
            "category": "Q&A",
            "answered": True,
        }
        assert discussion["type"] == "discussion"
        assert discussion["answered"] is True


class TestGitHubSignalMetaContracts:
    """Tests for GitHub Signal metadata contracts."""

    def test_valid_star_meta(self):
        """Test creating a valid star metadata dict."""
        star: GitHubStarMeta = {
            "starred_at": "2026-01-19T10:00:00Z",
            "repo_full_name": "org/repo",
        }
        assert star["starred_at"] == "2026-01-19T10:00:00Z"

    def test_valid_fork_meta(self):
        """Test creating a valid fork metadata dict."""
        fork: GitHubForkMeta = {
            "forked_at": "2026-01-19T11:00:00Z",
            "fork_full_name": "user/repo",
        }
        assert fork["fork_full_name"] == "user/repo"

    def test_valid_release_meta(self):
        """Test creating a valid release metadata dict."""
        release: GitHubReleaseMeta = {
            "tag_name": "v1.0.0",
            "name": "Version 1.0.0",
            "prerelease": False,
            "published_at": "2026-01-19T12:00:00Z",
        }
        assert release["tag_name"] == "v1.0.0"
        assert release["prerelease"] is False


class TestGitHubThreadMetaUnion:
    """Tests for GitHubThreadMeta union type."""

    def test_issue_is_valid_thread_meta(self):
        """Test that issue metadata is valid GitHubThreadMeta."""
        meta: GitHubThreadMeta = {
            "type": "issue",
            "number": 1,
            "state": "open",
            "html_url": "https://github.com/org/repo/issues/1",
            "labels": [],
            "author": {"login": "user", "id": 1},
        }
        assert meta["type"] == "issue"

    def test_pr_is_valid_thread_meta(self):
        """Test that PR metadata is valid GitHubThreadMeta."""
        meta: GitHubThreadMeta = {
            "type": "pull_request",
            "number": 1,
            "state": "open",
            "html_url": "https://github.com/org/repo/pull/1",
            "labels": [],
            "author": {"login": "user", "id": 1},
            "draft": False,
            "merged": False,
        }
        assert meta["type"] == "pull_request"


class TestGitHubSignalMetaUnion:
    """Tests for GitHubSignalMeta union type."""

    def test_star_is_valid_signal_meta(self):
        """Test that star metadata is valid GitHubSignalMeta."""
        meta: GitHubSignalMeta = {
            "starred_at": "2026-01-19T10:00:00Z",
            "repo_full_name": "org/repo",
        }
        assert meta["starred_at"] == "2026-01-19T10:00:00Z"

    def test_fork_is_valid_signal_meta(self):
        """Test that fork metadata is valid GitHubSignalMeta."""
        meta: GitHubSignalMeta = {
            "forked_at": "2026-01-19T10:00:00Z",
            "fork_full_name": "user/repo",
        }
        assert meta["forked_at"] == "2026-01-19T10:00:00Z"


@pytest.mark.django_db
class TestThreadTypedAccessor:
    """Tests for Thread.github typed accessor with real database."""

    def test_github_accessor_returns_typed_metadata(self):
        """Test that github accessor returns properly typed metadata."""
        metadata = {
            "github": {
                "type": "issue",
                "number": 123,
                "state": "open",
                "html_url": "https://github.com/org/repo/issues/123",
                "labels": [{"name": "bug", "color": "d73a4a"}],
                "author": {"login": "octocat", "id": 1},
            }
        }
        thread = ThreadFactory(metadata=metadata)

        # The accessor returns the typed metadata
        github_meta = thread.github
        assert github_meta is not None
        assert github_meta["number"] == 123
        assert github_meta["type"] == "issue"

    def test_github_accessor_returns_none_for_empty_metadata(self):
        """Test that github accessor returns None for empty metadata."""
        thread = ThreadFactory(metadata={})
        assert thread.github is None

    def test_github_accessor_returns_none_for_non_github_source(self):
        """Test that github accessor returns None for non-GitHub metadata."""
        thread = ThreadFactory(metadata={"slack": {"channel_id": "C123"}})
        assert thread.github is None


@pytest.mark.django_db
class TestSignalTypedAccessor:
    """Tests for Signal.github typed accessor with real database."""

    def test_github_accessor_returns_typed_metadata(self):
        """Test that github accessor returns properly typed metadata."""
        metadata = {
            "github": {
                "starred_at": "2026-01-19T10:00:00Z",
                "repo_full_name": "org/repo",
            }
        }
        signal = SignalFactory(signal_type="star", metadata=metadata)

        # The accessor returns the typed metadata
        github_meta = signal.github
        assert github_meta is not None
        assert github_meta["starred_at"] == "2026-01-19T10:00:00Z"

    def test_github_accessor_returns_none_for_empty_metadata(self):
        """Test that github accessor returns None for empty metadata."""
        signal = SignalFactory(metadata={})
        assert signal.github is None
