"""Keyword signal collector — monitors LinkedIn posts for watchlist keywords.

Polls `search_posts()` for each active watchlist, deduplicates against
existing signals, and saves new matches to the signals table.
Runs every 30 minutes via the scheduler.
"""

from __future__ import annotations

import json
import logging
import time
from typing import Any

logger = logging.getLogger(__name__)


async def collect_keyword_signals() -> str:
    """Collect signals from LinkedIn post searches for all active watchlists.

    For each active watchlist:
    1. Search LinkedIn posts using watchlist keywords
    2. Deduplicate against existing signals (same post_id + signal_type)
    3. Cross-reference post authors against campaign contacts
    4. Save new signals with metadata

    Returns summary string.
    """
    from ..constants import (
        SIGNAL_DAILY_KEYWORD_SEARCHES,
        SIGNAL_KEYWORD_MENTION,
        SIGNAL_COMPETITOR_MENTION,
        SIGNAL_TTL_KEYWORD_MENTION,
        SIGNAL_TTL_COMPETITOR_MENTION,
    )
    from ..db.signal_queries import (
        get_contact_by_linkedin_id,
        get_daily_signal_search_count,
        list_watchlists,
        save_signal,
        signal_exists,
        update_watchlist,
        upsert_signal_account,
    )
    from ..linkedin import UnipileError, get_account_id, get_linkedin_client

    account_id = get_account_id()
    if not account_id:
        return "No LinkedIn account connected."

    # Check daily rate limit
    daily_count = get_daily_signal_search_count("keyword")
    if daily_count >= SIGNAL_DAILY_KEYWORD_SEARCHES:
        return f"Daily keyword search limit reached ({daily_count}/{SIGNAL_DAILY_KEYWORD_SEARCHES})."

    # Get active watchlists
    watchlists = list_watchlists(is_active=True)
    if not watchlists:
        return "No active watchlists configured."

    try:
        client = get_linkedin_client()
    except UnipileError as e:
        return f"LinkedIn client error: {e}"

    total_new = 0
    total_searched = 0
    errors = 0
    now = int(time.time())

    for wl in watchlists:
        if daily_count + total_searched >= SIGNAL_DAILY_KEYWORD_SEARCHES:
            break

        wl_id = wl["id"]
        keywords_list = wl.get("keywords_list", [])
        watch_type = wl.get("watch_type", "keyword")
        campaign_id = wl.get("campaign_id")

        # Determine signal type based on watchlist type
        signal_type = SIGNAL_COMPETITOR_MENTION if watch_type == "competitor" else SIGNAL_KEYWORD_MENTION
        ttl = SIGNAL_TTL_COMPETITOR_MENTION if watch_type == "competitor" else SIGNAL_TTL_KEYWORD_MENTION

        for keyword in keywords_list:
            if daily_count + total_searched >= SIGNAL_DAILY_KEYWORD_SEARCHES:
                break

            try:
                posts, _ = await client.search_posts(account_id, keyword, limit=25)
                total_searched += 1

                for post in posts:
                    post_id = post.get("post_id", "")
                    post_text = post.get("text", "")
                    author_id = post.get("author_id", "")
                    author_name = post.get("author_name", "")
                    author_headline = post.get("author_headline", "")

                    if not post_id or not post_text:
                        continue

                    # Dedup: skip if we already have a signal for this post
                    if signal_exists(signal_type, post_id=post_id):
                        continue

                    # Cross-reference: check if author is a campaign contact
                    linked_prospect_id = None
                    linked_campaign_id = campaign_id
                    if author_id:
                        contact = get_contact_by_linkedin_id(author_id)
                        if contact:
                            linked_prospect_id = contact["id"]
                            linked_campaign_id = linked_campaign_id or contact.get("campaign_id")

                    # Build metadata
                    metadata = {
                        "keyword": keyword,
                        "watchlist_id": wl_id,
                        "watchlist_name": wl.get("name", ""),
                        "watch_type": watch_type,
                        "author_headline": author_headline,
                        "author_url": post.get("author_url", ""),
                        "reactions_count": post.get("reactions_count", 0),
                        "comments_count": post.get("comments_count", 0),
                        "timestamp": post.get("timestamp", ""),
                    }
                    if linked_prospect_id:
                        metadata["matched_contact"] = True

                    save_signal(
                        signal_type=signal_type,
                        source="keyword_search",
                        prospect_name=author_name or None,
                        prospect_title=author_headline or None,
                        linkedin_id=author_id or None,
                        prospect_id=linked_prospect_id,
                        campaign_id=linked_campaign_id,
                        content=post_text[:1000],  # Truncate for storage
                        post_id=post_id,
                        metadata_json=json.dumps(metadata),
                        expires_at=now + ttl,
                    )
                    total_new += 1

                    # Update signal account aggregation
                    if author_id:
                        upsert_signal_account(
                            linkedin_id=author_id,
                            prospect_name=author_name or None,
                        )

            except Exception as e:
                logger.warning("Keyword search failed for '%s': %s", keyword, e)
                errors += 1

        # Update last polled timestamp
        update_watchlist(wl_id, last_polled_at=now)

    await client.close()

    summary = f"Searched {total_searched} keywords, found {total_new} new signals"
    if errors:
        summary += f", {errors} errors"
    return summary
