"""
introd/groq_draft.py - groq llama 4 maverick for smart intro drafting

uses groq api to generate personalized, natural intro messages
that don't sound like ai-generated slop
"""

import os
import json
import requests
from datetime import datetime

GROQ_API_KEY = os.environ.get('GROQ_API_KEY', '')
GROQ_API_URL = 'https://api.groq.com/openai/v1/chat/completions'
MODEL = os.environ.get('GROQ_MODEL', 'llama-3.1-70b-versatile')


def determine_contact_method(human):
    """
    determine best contact method based on WHERE THEY'RE MOST ACTIVE

    don't use fixed hierarchy - analyze activity per platform:
    - count posts/commits/activity
    - weight by recency (last 30 days matters more)
    - contact them where they already are
    - fall back to email only if no social activity
    """
    from datetime import datetime, timedelta

    extra = human.get('extra', {})
    if isinstance(extra, str):
        extra = json.loads(extra) if extra else {}

    # handle nested extra.extra from old save format
    if 'extra' in extra and isinstance(extra['extra'], dict):
        extra = {**extra, **extra['extra']}

    contact = human.get('contact', {})
    if isinstance(contact, str):
        contact = json.loads(contact) if contact else {}

    # collect activity scores per platform
    activity_scores = {}
    now = datetime.now()
    thirty_days_ago = now - timedelta(days=30)
    ninety_days_ago = now - timedelta(days=90)

    # github activity
    github_username = human.get('username') if human.get('platform') == 'github' else extra.get('github')
    if github_username:
        github_score = 0
        top_repos = extra.get('top_repos', [])

        for repo in top_repos:
            # recent commits weight more
            pushed_at = repo.get('pushed_at', '')
            if pushed_at:
                try:
                    push_date = datetime.fromisoformat(pushed_at.replace('Z', '+00:00')).replace(tzinfo=None)
                    if push_date > thirty_days_ago:
                        github_score += 10  # very recent
                    elif push_date > ninety_days_ago:
                        github_score += 5   # somewhat recent
                    else:
                        github_score += 1   # old but exists
                except:
                    github_score += 1

            # stars indicate engagement
            github_score += min(repo.get('stars', 0) // 10, 5)

        # commit activity from deep scrape
        commit_count = extra.get('commit_count', 0)
        github_score += min(commit_count // 10, 20)

        if github_score > 0:
            activity_scores['github_issue'] = {
                'score': github_score,
                'info': f"{github_username}/{top_repos[0]['name']}" if top_repos else github_username
            }

    # mastodon activity
    mastodon_handle = extra.get('mastodon') or contact.get('mastodon')
    if mastodon_handle:
        mastodon_score = 0
        statuses_count = extra.get('mastodon_statuses', 0) or human.get('statuses_count', 0)

        # high post count = active user
        if statuses_count > 1000:
            mastodon_score += 30
        elif statuses_count > 500:
            mastodon_score += 20
        elif statuses_count > 100:
            mastodon_score += 10
        elif statuses_count > 0:
            mastodon_score += 5

        # platform bonus for fediverse (values-aligned)
        mastodon_score += 10

        # bonus if handle was discovered via rel="me" or similar verification
        # (having a handle linked from their website = they want to be contacted there)
        handles = extra.get('handles', {})
        if handles.get('mastodon') == mastodon_handle:
            mastodon_score += 15  # verified handle bonus

        if mastodon_score > 0:
            activity_scores['mastodon'] = {'score': mastodon_score, 'info': mastodon_handle}

    # bluesky activity
    bluesky_handle = extra.get('bluesky') or contact.get('bluesky')
    if bluesky_handle:
        bluesky_score = 0
        posts_count = extra.get('bluesky_posts', 0) or human.get('posts_count', 0)

        if posts_count > 500:
            bluesky_score += 25
        elif posts_count > 100:
            bluesky_score += 15
        elif posts_count > 0:
            bluesky_score += 5

        # newer platform, slightly lower weight
        bluesky_score += 5

        if bluesky_score > 0:
            activity_scores['bluesky'] = {'score': bluesky_score, 'info': bluesky_handle}

    # twitter activity
    twitter_handle = extra.get('twitter') or contact.get('twitter')
    if twitter_handle:
        twitter_score = 0
        tweets_count = extra.get('twitter_tweets', 0)

        if tweets_count > 1000:
            twitter_score += 20
        elif tweets_count > 100:
            twitter_score += 10
        elif tweets_count > 0:
            twitter_score += 5

        # if we found them via twitter hashtags, they're active there
        if human.get('platform') == 'twitter':
            twitter_score += 15

        if twitter_score > 0:
            activity_scores['twitter'] = {'score': twitter_score, 'info': twitter_handle}

    # NOTE: reddit is DISCOVERY ONLY, not a contact method
    # we find users on reddit but reach out via their external links (github, mastodon, etc.)
    # reddit-only users go to manual_queue for review

    # lobsters activity
    lobsters_username = extra.get('lobsters') or contact.get('lobsters')
    if lobsters_username or human.get('platform') == 'lobsters':
        lobsters_score = 0
        lobsters_username = lobsters_username or human.get('username')

        karma = extra.get('lobsters_karma', 0) or human.get('karma', 0)

        # lobsters is invite-only, high signal
        lobsters_score += 15

        if karma > 100:
            lobsters_score += 15
        elif karma > 50:
            lobsters_score += 10
        elif karma > 0:
            lobsters_score += 5

        if lobsters_score > 0:
            activity_scores['lobsters'] = {'score': lobsters_score, 'info': lobsters_username}

    # matrix activity
    matrix_id = extra.get('matrix') or contact.get('matrix')
    if matrix_id:
        matrix_score = 0

        # matrix users are typically privacy-conscious and technical
        matrix_score += 15  # platform bonus for decentralized chat

        # bonus if handle was discovered via rel="me" verification
        handles = extra.get('handles', {})
        if handles.get('matrix') == matrix_id:
            matrix_score += 10  # verified handle bonus

        if matrix_score > 0:
            activity_scores['matrix'] = {'score': matrix_score, 'info': matrix_id}

    # lemmy activity (fediverse)
    lemmy_username = human.get('username') if human.get('platform') == 'lemmy' else extra.get('lemmy')
    if lemmy_username:
        lemmy_score = 0

        # lemmy is fediverse - high values alignment
        lemmy_score += 20  # fediverse platform bonus

        post_count = extra.get('post_count', 0)
        comment_count = extra.get('comment_count', 0)

        if post_count > 100:
            lemmy_score += 15
        elif post_count > 50:
            lemmy_score += 10
        elif post_count > 10:
            lemmy_score += 5

        if comment_count > 500:
            lemmy_score += 10
        elif comment_count > 100:
            lemmy_score += 5

        if lemmy_score > 0:
            activity_scores['lemmy'] = {'score': lemmy_score, 'info': lemmy_username}

    # pick highest activity platform
    if activity_scores:
        best_platform = max(activity_scores.items(), key=lambda x: x[1]['score'])
        return best_platform[0], best_platform[1]['info']

    # fall back to email ONLY if no social activity detected
    email = extra.get('email') or contact.get('email')
    # also check emails list
    if not email:
        emails = extra.get('emails') or contact.get('emails') or []
        for e in emails:
            if e and '@' in e and 'noreply' not in e.lower():
                email = e
                break

    if email and '@' in email and 'noreply' not in email.lower():
        return 'email', email

    # last resort: manual
    return 'manual', None


def draft_intro_with_llm(match_data, recipient='a', dry_run=False):
    """
    use groq llama 4 maverick to draft a personalized intro

    match_data should contain:
    - human_a: the first person
    - human_b: the second person
    - overlap_score: numeric score
    - overlap_reasons: list of why they match

    recipient: 'a' or 'b' - who we're writing to
    """
    if not GROQ_API_KEY:
        return None, "GROQ_API_KEY not set"

    # determine recipient and other person
    if recipient == 'a':
        to_person = match_data.get('human_a', {})
        other_person = match_data.get('human_b', {})
    else:
        to_person = match_data.get('human_b', {})
        other_person = match_data.get('human_a', {})

    # build context
    to_name = to_person.get('name') or to_person.get('username', 'friend')
    other_name = other_person.get('name') or other_person.get('username', 'someone')

    to_signals = to_person.get('signals', [])
    if isinstance(to_signals, str):
        to_signals = json.loads(to_signals) if to_signals else []

    other_signals = other_person.get('signals', [])
    if isinstance(other_signals, str):
        other_signals = json.loads(other_signals) if other_signals else []

    overlap_reasons = match_data.get('overlap_reasons', [])
    if isinstance(overlap_reasons, str):
        overlap_reasons = json.loads(overlap_reasons) if overlap_reasons else []

    # parse extra data
    to_extra = to_person.get('extra', {})
    other_extra = other_person.get('extra', {})
    if isinstance(to_extra, str):
        to_extra = json.loads(to_extra) if to_extra else {}
    if isinstance(other_extra, str):
        other_extra = json.loads(other_extra) if other_extra else {}

    # build profile summaries
    to_profile = f"""
name: {to_name}
platform: {to_person.get('platform', 'unknown')}
bio: {to_person.get('bio') or 'no bio'}
location: {to_person.get('location') or 'unknown'}
signals: {', '.join(to_signals[:8])}
repos: {len(to_extra.get('top_repos', []))} public repos
languages: {', '.join(to_extra.get('languages', {}).keys())}
"""

    other_profile = f"""
name: {other_name}
platform: {other_person.get('platform', 'unknown')}
bio: {other_person.get('bio') or 'no bio'}
location: {other_person.get('location') or 'unknown'}
signals: {', '.join(other_signals[:8])}
repos: {len(other_extra.get('top_repos', []))} public repos
languages: {', '.join(other_extra.get('languages', {}).keys())}
url: {other_person.get('url', '')}
"""

    # build prompt
    system_prompt = """you are connectd, an ai that connects isolated builders who share values but don't know each other yet.

your job is to write a short, genuine intro message to one person about another person they might want to know.

rules:
- be brief (3-5 sentences max)
- be genuine, not salesy or fake
- focus on WHY they might want to connect, not just WHAT they have in common
- don't be cringe or use buzzwords
- lowercase preferred (casual tone)
- no emojis unless the person's profile suggests they'd like them
- mention specific things from their profiles, not generic "you both like open source"
- end with a simple invitation, not a hard sell
- sign off as "- connectd" (lowercase)

bad examples:
- "I noticed you're both passionate about..." (too formal)
- "You two would be PERFECT for each other!" (too salesy)
- "As a fellow privacy enthusiast..." (cringe)

good examples:
- "hey, saw you're building X. there's someone else working on similar stuff in Y who might be interesting to know."
- "you might want to check out Z's work on federated systems - similar approach to what you're doing with A."
"""

    user_prompt = f"""write an intro message to {to_name} about {other_name}.

RECIPIENT ({to_name}):
{to_profile}

INTRODUCING ({other_name}):
{other_profile}

WHY THEY MATCH (overlap score {match_data.get('overlap_score', 0)}):
{', '.join(overlap_reasons[:5])}

write a short intro message. remember: lowercase, genuine, not salesy."""

    try:
        response = requests.post(
            GROQ_API_URL,
            headers={
                'Authorization': f'Bearer {GROQ_API_KEY}',
                'Content-Type': 'application/json',
            },
            json={
                'model': MODEL,
                'messages': [
                    {'role': 'system', 'content': system_prompt},
                    {'role': 'user', 'content': user_prompt},
                ],
                'temperature': 0.7,
                'max_tokens': 300,
            },
            timeout=30,
        )

        if response.status_code != 200:
            return None, f"groq api error: {response.status_code} - {response.text}"

        data = response.json()
        draft = data['choices'][0]['message']['content'].strip()

        # determine contact method for recipient
        contact_method, contact_info = determine_contact_method(to_person)

        return {
            'draft': draft,
            'model': MODEL,
            'to': to_name,
            'about': other_name,
            'overlap_score': match_data.get('overlap_score', 0),
            'contact_method': contact_method,
            'contact_info': contact_info,
            'generated_at': datetime.now().isoformat(),
        }, None

    except Exception as e:
        return None, f"groq error: {str(e)}"


def draft_intro_batch(matches, dry_run=False):
    """
    draft intros for multiple matches
    returns list of (match, intro_result, error) tuples
    """
    results = []

    for match in matches:
        # draft for both directions
        intro_a, err_a = draft_intro_with_llm(match, recipient='a', dry_run=dry_run)
        intro_b, err_b = draft_intro_with_llm(match, recipient='b', dry_run=dry_run)

        results.append({
            'match': match,
            'intro_to_a': intro_a,
            'intro_to_b': intro_b,
            'errors': [err_a, err_b],
        })

    return results


def test_groq_connection():
    """test that groq api is working"""
    if not GROQ_API_KEY:
        return False, "GROQ_API_KEY not set"

    try:
        response = requests.post(
            GROQ_API_URL,
            headers={
                'Authorization': f'Bearer {GROQ_API_KEY}',
                'Content-Type': 'application/json',
            },
            json={
                'model': MODEL,
                'messages': [{'role': 'user', 'content': 'say "ok" and nothing else'}],
                'max_tokens': 10,
            },
            timeout=10,
        )

        if response.status_code == 200:
            return True, "groq api working"
        else:
            return False, f"groq api error: {response.status_code}"

    except Exception as e:
        return False, f"groq connection error: {str(e)}"
