# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
GitHub Channel (Notification Polling)

Monitor GitHub notifications (issues, PRs) and respond as the Familiar agent.
Uses polling via PyGithub — no public URL needed.

Features:
- Poll participating notifications (mentions, assignments, review requests)
- Respond to issues and pull requests
- Filter by repository
- Configurable poll interval

Setup:
1. Create a Personal Access Token at https://github.com/settings/tokens
2. Required scopes: repo, notifications
3. Set GITHUB_TOKEN environment variable
"""

import logging
import os
import time
from datetime import datetime, timezone
from typing import Optional

logger = logging.getLogger(__name__)

from .auth import ChannelType, ChannelUser, create_authenticator  # noqa: E402
from .connect_wizard import ConnectMixin  # noqa: E402
from .formatting import FormatMode  # noqa: E402


class GitHubChannel(ConnectMixin):
    """
    GitHub integration for Familiar via notification polling.

    Usage:
        channel = GitHubChannel(agent)
        channel.run()  # Blocking — polls in a loop
    """

    # ConnectMixin adapter properties
    format_mode = FormatMode.MARKDOWN  # GitHub Flavored Markdown
    supports_buttons = False
    supports_message_deletion = False

    def __init__(
        self,
        agent,
        token: str = None,
        repos: list = None,
        poll_interval: int = None,
    ):
        """
        Initialize GitHub channel.

        Args:
            agent: The Familiar instance
            token: GitHub PAT (or use GITHUB_TOKEN env var)
            repos: Filter to specific repos (e.g. ["owner/repo1", "owner/repo2"])
            poll_interval: Seconds between notification checks (default: 60)
        """
        self.agent = agent
        self.token = token or os.environ.get("GITHUB_TOKEN", "")
        self.poll_interval = poll_interval or int(os.environ.get("GITHUB_POLL_INTERVAL", "60"))

        # Repo filter
        repos_env = os.environ.get("GITHUB_REPOS", "")
        self.repos = repos or [r.strip() for r in repos_env.split(",") if r.strip()]

        self.github = None
        self.github_user = None
        self._last_poll = datetime.now(timezone.utc)

        # Phase 1: Multi-user authentication
        self.authenticator = create_authenticator(
            config=agent.config if hasattr(agent, "config") else None,
        )

    # ── ConnectMixin adapter methods (no-ops — GitHub isn't interactive) ──

    async def wizard_send(self, recipient_id: str, text: str) -> None:
        # GitHub wizard runs from other channels (CLI, Telegram, dashboard)
        pass

    async def wizard_send_menu(
        self, recipient_id: str, text: str, options: list[tuple[str, str]]
    ) -> None:
        pass

    async def wizard_delete_message(self, recipient_id: str, message_ref) -> bool:
        return False

    def _get_channel_user(self, github_login: str) -> Optional[ChannelUser]:
        """Get authenticated ChannelUser for GitHub user."""
        return self.authenticator.authenticate(
            channel_type=ChannelType.GITHUB,
            channel_id=str(github_login),
            display_name=github_login,
        )

    def _setup_client(self):
        """Set up PyGithub client."""
        try:
            from github import Github
        except ImportError:
            raise RuntimeError("PyGithub not installed. Install with: pip install PyGithub")

        self.github = Github(self.token)
        self.github_user = self.github.get_user()
        logger.info(f"GitHub authenticated as {self.github_user.login}")

    def _poll_notifications(self):
        """Fetch and process new participating notifications."""
        try:
            notifications = self.github.get_user().get_notifications(
                participating=True,
                since=self._last_poll,
            )

            self._last_poll = datetime.now(timezone.utc)

            for notif in notifications:
                try:
                    self._handle_notification(notif)
                except Exception as e:
                    logger.error(f"Error handling notification {notif.id}: {e}")

        except Exception as e:
            logger.error(f"Error polling GitHub notifications: {e}")

    def _handle_notification(self, notif):
        """Process a single notification."""
        reason = notif.reason  # mention, assign, review_requested, etc.
        subject_type = notif.subject.type  # Issue, PullRequest, etc.
        repo_name = notif.repository.full_name

        # Apply repo filter
        if self.repos and repo_name not in self.repos:
            logger.debug(f"Skipping notification for {repo_name} (not in filter)")
            notif.mark_as_read()
            return

        # Only handle issues and PRs
        if subject_type == "Issue":
            self._respond_to_issue(notif, repo_name, reason)
        elif subject_type == "PullRequest":
            self._respond_to_pr(notif, repo_name, reason)
        else:
            logger.debug(f"Skipping {subject_type} notification for {repo_name}")

        # Mark as read
        notif.mark_as_read()

    def _respond_to_issue(self, notif, repo_name: str, reason: str):
        """Build context from an issue and respond."""
        try:
            repo = self.github.get_repo(repo_name)

            # Extract issue number from URL
            issue_url = notif.subject.url
            issue_number = int(issue_url.rsplit("/", 1)[-1])
            issue = repo.get_issue(issue_number)

            # Build context for the agent
            labels = ", ".join(lb.name for lb in issue.labels) or "none"
            comments = list(issue.get_comments())
            recent_comments = comments[-5:] if len(comments) > 5 else comments

            comment_text = ""
            for c in recent_comments:
                comment_text += f"\n- @{c.user.login}: {c.body[:200]}"

            context = (
                f"You are responding to a GitHub issue. "
                f"Be helpful and concise. Use GitHub Flavored Markdown.\n\n"
                f"Repository: {repo_name}\n"
                f"Issue #{issue_number}: {issue.title}\n"
                f"State: {issue.state}\n"
                f"Labels: {labels}\n"
                f"Author: @{issue.user.login}\n"
                f"Reason you were notified: {reason}\n\n"
                f"Issue body:\n{(issue.body or '(empty)')[:2000]}\n"
            )

            if comment_text:
                context += f"\nRecent comments:{comment_text}\n"

            context += (
                "\nRespond to this issue. "
                "If you were mentioned, address the question directly. "
                "If assigned, provide analysis or a solution."
            )

            # Auto-promote owner
            github_owner = os.environ.get("OWNER_GITHUB_ID")
            user_id = f"github:{issue.user.login}"
            if github_owner and issue.user.login == github_owner:
                try:
                    from ..core.security import TrustLevel

                    session = self.agent.sessions.get_or_create_session(user_id, "github")
                    if session.trust_level != TrustLevel.OWNER:
                        session.set_trust_level(TrustLevel.OWNER)
                        session.daily_budget = 50.0
                        self.agent.sessions.save_session(session)
                except ImportError as e:
                    logger.debug("Optional dependency not available: %s", e)
            logger.info(f"[GitHub] Responding to issue #{issue_number} in {repo_name} ({reason})")

            response = self.agent.chat(
                message=context,
                user_id=user_id,
                channel=f"github:{repo_name}",
            )

            if response:
                issue.create_comment(response)
                logger.info(f"[GitHub] Posted comment on {repo_name}#{issue_number}")

        except Exception as e:
            logger.error(f"Error responding to issue in {repo_name}: {e}")

    def _respond_to_pr(self, notif, repo_name: str, reason: str):
        """Build context from a pull request and respond."""
        try:
            repo = self.github.get_repo(repo_name)

            # Extract PR number from URL
            pr_url = notif.subject.url
            pr_number = int(pr_url.rsplit("/", 1)[-1])
            pr = repo.get_pull(pr_number)

            # Build context for the agent
            labels = ", ".join(lb.name for lb in pr.labels) or "none"
            comments = list(pr.get_issue_comments())
            recent_comments = comments[-5:] if len(comments) > 5 else comments

            comment_text = ""
            for c in recent_comments:
                comment_text += f"\n- @{c.user.login}: {c.body[:200]}"

            context = (
                f"You are responding to a GitHub pull request. "
                f"Be helpful and concise. Use GitHub Flavored Markdown.\n\n"
                f"Repository: {repo_name}\n"
                f"PR #{pr_number}: {pr.title}\n"
                f"State: {pr.state}\n"
                f"Labels: {labels}\n"
                f"Author: @{pr.user.login}\n"
                f"Branch: {pr.head.ref} → {pr.base.ref}\n"
                f"Changes: +{pr.additions} -{pr.deletions} ({pr.changed_files} files)\n"
                f"Reason you were notified: {reason}\n\n"
                f"PR body:\n{(pr.body or '(empty)')[:2000]}\n"
            )

            if comment_text:
                context += f"\nRecent comments:{comment_text}\n"

            context += (
                "\nRespond to this pull request. "
                "If you were requested for review, provide a code review. "
                "If mentioned, address the question directly."
            )

            user_id = f"github:{pr.user.login}"
            logger.info(f"[GitHub] Responding to PR #{pr_number} in {repo_name} ({reason})")

            response = self.agent.chat(
                message=context,
                user_id=user_id,
                channel=f"github:{repo_name}",
            )

            if response:
                pr.create_issue_comment(response)
                logger.info(f"[GitHub] Posted comment on {repo_name}#{pr_number}")

        except Exception as e:
            logger.error(f"Error responding to PR in {repo_name}: {e}")

    def run(self):
        """Start the GitHub notification poller (blocking)."""
        if not self.token:
            print("❌ GitHub token not set")
            print("   Set GITHUB_TOKEN environment variable")
            print("   Create one at https://github.com/settings/tokens")
            return

        self._setup_client()

        repo_info = f" (repos: {', '.join(self.repos)})" if self.repos else " (all repos)"
        print("🚀 Starting GitHub notification poller...")
        print(f"   User: {self.github_user.login}")
        print(f"   Interval: {self.poll_interval}s")
        print(f"   Filter:{repo_info}")

        # Start scheduler if available
        if hasattr(self, "agent") and hasattr(self.agent, "start_scheduler"):
            self.agent.start_scheduler()

        try:
            while True:
                self._poll_notifications()
                time.sleep(self.poll_interval)
        except KeyboardInterrupt:
            print("\nShutting down GitHub poller...")
