import sys
import os
vendor_path = os.path.join(os.path.dirname(__file__), 'vendor')
if vendor_path not in sys.path:
    sys.path.insert(0, vendor_path)

import logging
import time
from datetime import datetime

from x_sheets_writer import XSheetsManager
from x_api_client import XApiClient

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler("x_automation.log", encoding="utf-8"),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

CREDS_FILE = os.path.join(os.path.dirname(__file__), "credentials.json")
SPREADSHEET_ID = "1icqi5ZjqjRUOcGnxtDyHw1-gXj-osmGjB8twUnKB6i0"


def u(value: str) -> str:
    return value.encode("ascii").decode("unicode_escape")


def str_to_int(val, default=0):
    try:
        return int(val)
    except Exception:
        return default


def is_true(val):
    if isinstance(val, bool):
        return val
    return str(val).upper() == "TRUE"


def split_words(value):
    if not value:
        return []
    return [w.strip() for w in str(value).split(",") if w.strip()]


# These defaults are intentionally simple rule-based heuristics.
# Tune them from the spreadsheet first; edit code only when the scoring model itself changes.
DEFAULT_LIKE_POSITIVE_KEYWORDS = u(
    "\\u4ed5\\u4e8b,\\u8077\\u5834,\\u4f1a\\u793e,\\u51fa\\u793e,\\u96fb\\u8a71\\u5bfe\\u5fdc,"
    "\\u96d1\\u8ac7,\\u4eba\\u9593\\u95a2\\u4fc2,\\u30b1\\u30a2\\u30ec\\u30b9\\u30df\\u30b9,"
    "\\u8074\\u899a\\u904e\\u654f,\\u611f\\u899a\\u904e\\u654f,\\u4e88\\u5b9a\\u5909\\u66f4,"
    "\\u5411\\u3044\\u3066\\u306a\\u3044,\\u3057\\u3093\\u3069\\u3044,\\u75b2\\u308c\\u308b,\\u6016\\u3044"
)
DEFAULT_PROFILE_POSITIVE_KEYWORDS = u(
    "ADHD,ASD,\\u767a\\u9054\\u969c\\u5bb3,\\u767a\\u9054\\u30b0\\u30ec\\u30fc,"
    "\\u30b0\\u30ec\\u30fc\\u30be\\u30fc\\u30f3,HSP,\\u672a\\u8a3a\\u65ad,\\u5f53\\u4e8b\\u8005,\\u7279\\u6027"
)
DEFAULT_LOCAL_KEYWORDS = u(
    "\\u5927\\u962a,\\u95a2\\u897f,\\u5175\\u5eab,\\u4eac\\u90fd,\\u5948\\u826f,\\u5439\\u7530,"
    "\\u8c4a\\u4e2d,\\u7b95\\u9762,\\u8328\\u6728,\\u9ad8\\u69fb,\\u6442\\u6d25,\\u5317\\u6442,"
    "\\u6885\\u7530,\\u6c5f\\u5742,\\u5343\\u91cc,\\u5fa1\\u5802\\u7b4b\\u7dda,\\u962a\\u6025"
)
DEFAULT_HARD_EXCLUDE_KEYWORDS = u(
    "\\u6c42\\u4eba,\\u63a1\\u7528,\\u526f\\u696d,\\u30a2\\u30d5\\u30a3\\u30ea,PR,"
    "\\u7121\\u6599\\u76f8\\u8ac7,\\u30bb\\u30df\\u30ca\\u30fc,\\u8b1b\\u5ea7,\\u8d77\\u696d,"
    "\\u652f\\u63f4\\u54e1,\\u30ab\\u30a6\\u30f3\\u30bb\\u30e9\\u30fc,\\u5fc3\\u7406\\u58eb,"
    "\\u533b\\u5e2b,\\u770b\\u8b77\\u5e2b,\\u9ad8\\u6821\\u751f,\\u5927\\u5b66\\u751f,"
    "\\u4e2d\\u5b66\\u751f,\\u5c0f\\u5b66\\u751f,\\u606f\\u5b50,\\u5a18,\\u5b50\\u3069\\u3082,\\u5b50\\u4f9b"
)
DEFAULT_SENSITIVE_EXCLUDE_KEYWORDS = u(
    "\\u6b7b\\u306b\\u305f\\u3044,\\u6d88\\u3048\\u305f\\u3044,\\u81ea\\u6bba,"
    "\\u5e0c\\u6b7b\\u5ff5\\u616e,OD,\\u30ea\\u30b9\\u30ab,\\u9996\\u540a\\u308a,\\u6bba\\u3057\\u305f\\u3044"
)


def setting_words(settings, key, default_value):
    return split_words(settings.get(key, default_value))


def match_words(text, words):
    if not text:
        return []
    return [w for w in words if w and w in text]


def compact_reason(label, hits, limit=3):
    if not hits:
        return ""
    shown = hits[:limit]
    suffix = "..." if len(hits) > limit else ""
    return f"{label}:{'/'.join(shown)}{suffix}"


def score_candidate(tweet, settings, fallback_local_words):
    """Rule-based candidate scoring.

    The goal is not perfect classification. It is a stable, editable first pass that
    prevents broad search results from turning directly into likes/follows.
    Spreadsheet settings override all keyword lists and thresholds.
    """
    text = str(tweet.get("tweet_text", ""))
    profile = str(tweet.get("profile", ""))
    combined = f"{text}\n{profile}"

    like_words = setting_words(settings, "scoring_like_positive_keywords", DEFAULT_LIKE_POSITIVE_KEYWORDS)
    profile_words = setting_words(settings, "scoring_profile_positive_keywords", DEFAULT_PROFILE_POSITIVE_KEYWORDS)
    local_words = setting_words(settings, "scoring_local_keywords", ",".join(fallback_local_words) or DEFAULT_LOCAL_KEYWORDS)
    hard_exclude_words = setting_words(settings, "scoring_hard_exclude_keywords", DEFAULT_HARD_EXCLUDE_KEYWORDS)
    sensitive_words = setting_words(settings, "scoring_sensitive_exclude_keywords", DEFAULT_SENSITIVE_EXCLUDE_KEYWORDS)

    min_like_score = str_to_int(settings.get("min_like_score"), 3)
    min_follow_score = str_to_int(settings.get("min_follow_score"), 7)

    post_score = 0
    account_score = 0
    reasons = []
    exclude_reasons = []

    sensitive_hits = match_words(text, sensitive_words)
    hard_hits = match_words(combined, hard_exclude_words)
    if sensitive_hits:
        exclude_reasons.append(compact_reason(u("\\u91cd\\u3044\\u6295\\u7a3f"), sensitive_hits))
    if hard_hits:
        exclude_reasons.append(compact_reason(u("\\u5bfe\\u8c61\\u5916"), hard_hits))

    like_hits = match_words(text, like_words)
    if like_hits:
        n = min(len(like_hits), 3)
        post_score += n * 2
        account_score += n
        reasons.append(compact_reason(u("\\u6295\\u7a3f"), like_hits))

    profile_hits = match_words(profile, profile_words)
    if profile_hits:
        n = min(len(profile_hits), 2)
        post_score += 1
        account_score += n * 3
        reasons.append(compact_reason(u("\\u30d7\\u30ed\\u30d5"), profile_hits))

    local_hits = match_words(combined, local_words)
    if local_hits:
        post_score += 1
        account_score += 3
        reasons.append(compact_reason(u("\\u5730\\u57df"), local_hits))

    if len(text.strip()) < 10:
        post_score -= 1
        reasons.append(u("\\u77ed\\u6587"))

    excluded = bool(exclude_reasons)
    like_recommended = (not excluded) and post_score >= min_like_score
    follow_recommended = (not excluded) and account_score >= min_follow_score

    return {
        "post_score": post_score,
        "account_score": account_score,
        "like_recommended": like_recommended,
        "follow_recommended": follow_recommended,
        "excluded": excluded,
        "reason": ", ".join([r for r in reasons if r]),
        "exclude_reason": ", ".join([r for r in exclude_reasons if r]),
    }


def sort_candidates(candidates):
    return sorted(
        candidates,
        key=lambda t: (
            t["score_info"]["excluded"],
            not t["score_info"]["follow_recommended"],
            not t["score_info"]["like_recommended"],
            -t["score_info"]["account_score"],
            -t["score_info"]["post_score"],
        )
    )


def main():
    logger.info("=" * 45)
    logger.info("Starting X discovery tool (scored search, like, follow logging)")
    logger.info("=" * 45)

    try:
        sheet_mgr = XSheetsManager(CREDS_FILE, SPREADSHEET_ID)
        settings = sheet_mgr.get_settings()

        pause_follow = is_true(settings.get("pause_follow", False))
        pause_like = is_true(settings.get("pause_like", settings.get("pause_tweet", False)))
        scoring_enabled = is_true(settings.get("scoring_enabled", True))

        if pause_follow:
            logger.warning("pause_follow=TRUE: follow actions are skipped.")
        if pause_like:
            logger.warning("pause_like=TRUE: like actions are skipped.")

        api_client = XApiClient(settings)
        generate_reply_drafts = is_true(settings.get("generate_reply_drafts", False))
        ai_generator = None
        if generate_reply_drafts:
            from comment_generator import CommentGeneratorFactory
            ai_generator = CommentGeneratorFactory.get_generator("gemini", settings)
            logger.info("Reply draft generation: enabled")
        else:
            logger.info("Reply draft generation: disabled by no-reply operation policy")

        search_limit = str_to_int(settings.get("search_limit"), 10)
        like_limit_per_run = str_to_int(settings.get("like_limit_per_run"), 3)
        follow_limit_per_run = str_to_int(settings.get("follow_limit_per_run"), 2)
        sleep_interval = str_to_int(settings.get("sleep_interval"), 3)
        follow_mode = str(settings.get("follow_mode", ""))
        like_mode = str(settings.get("like_mode", ""))
        follow_mode_auto = u("\\u81ea\\u52d5") in follow_mode
        like_mode_auto = u("\\u81ea\\u52d5") in like_mode
        should_queue_follow = (not pause_follow and follow_limit_per_run > 0 and follow_mode_auto)

        profile_filter_words = split_words(settings.get("profile_filter_keywords", ""))

        settings["search_limit"] = search_limit
        logger.info(
            f"Run limits: search={search_limit}, likes={like_limit_per_run}, follows={follow_limit_per_run}, scoring={scoring_enabled}"
        )

        tweets = api_client.search_tweets(settings)
        if not tweets:
            logger.info("No new tweets matched the current search conditions.")
            return

        registered_accounts = sheet_mgr.get_registered_accounts()
        candidates = []
        skipped_registered = 0
        skipped_ng_account = 0

        for tweet in tweets:
            username = tweet["username"]
            if username in registered_accounts:
                skipped_registered += 1
                continue

            account_info = registered_accounts.get(username, {})
            account_status = account_info.get("flg_final", "")
            if str(account_status).upper() == "FALSE":
                skipped_ng_account += 1
                continue

            if scoring_enabled:
                score_info = score_candidate(tweet, settings, profile_filter_words)
            else:
                score_info = {
                    "post_score": 0,
                    "account_score": 0,
                    "like_recommended": True,
                    "follow_recommended": True,
                    "excluded": False,
                    "reason": "scoring_disabled",
                    "exclude_reason": "",
                }
            tweet["score_info"] = score_info
            candidates.append(tweet)

        candidates = sort_candidates(candidates)
        logger.info(
            f"Candidates: usable={len(candidates)}, skipped_registered={skipped_registered}, skipped_ng_account={skipped_ng_account}"
        )

        like_count = 0
        follow_count = 0
        prompt_template = settings.get("ai_prompt", "")
        processed_count = 0

        for tweet in candidates:
            username = tweet["username"]
            tweet_id = tweet["tweet_id"]
            author_id = tweet["author_id"]
            score_info = tweet["score_info"]

            logger.info(
                f"Candidate {username}: post_score={score_info['post_score']}, account_score={score_info['account_score']}, "
                f"like={score_info['like_recommended']}, follow={score_info['follow_recommended']}, excluded={score_info['excluded']}"
            )

            like_result = u("\\u672a\\u51e6\\u7406")
            if score_info["excluded"]:
                like_result = u("\\u5bfe\\u8c61\\u5916(\\u9664\\u5916)")
            elif not score_info["like_recommended"]:
                like_result = u("\\u5bfe\\u8c61\\u5916(\\u30b9\\u30b3\\u30a2\\u4e0d\\u8db3)")
            elif pause_like or not like_mode_auto:
                like_result = u("\\u505c\\u6b62\\u4e2d")
            elif like_count >= like_limit_per_run:
                like_result = u("\\u672a\\u51e6\\u7406(\\u3044\\u3044\\u306d\\u4e0a\\u9650)")
            elif is_true(settings.get("test_like", False)):
                like_result = u("\\u6210\\u529f(\\u30c6\\u30b9\\u30c8\\u30b9\\u30ad\\u30c3\\u30d7)")
                like_count += 1
            else:
                like_result = api_client.like_tweet(tweet_id)
                like_count += 1

            follow_result = u("\\u672a\\u51e6\\u7406")
            follow_flag = should_queue_follow and score_info["follow_recommended"]
            if score_info["excluded"]:
                follow_result = u("\\u5bfe\\u8c61\\u5916(\\u9664\\u5916)")
            elif not score_info["follow_recommended"]:
                follow_result = u("\\u5bfe\\u8c61\\u5916(\\u30b9\\u30b3\\u30a2\\u4e0d\\u8db3)")
            elif not should_queue_follow:
                follow_result = u("\\u505c\\u6b62\\u4e2d")
            elif follow_count < follow_limit_per_run:
                if is_true(settings.get("test_follow", False)):
                    follow_result = u("\\u6210\\u529f(\\u30c6\\u30b9\\u30c8\\u30b9\\u30ad\\u30c3\\u30d7)")
                    follow_count += 1
                else:
                    follow_result = api_client.follow_user(author_id)
                    follow_count += 1
            else:
                follow_result = u("\\u672a\\u51e6\\u7406(\\u30d5\\u30a9\\u30ed\\u30fc\\u5f85\\u6a5f)")

            ai_comment = ""
            if generate_reply_drafts and ai_generator:
                ai_comment = ai_generator.generate_reply(
                    tweet_text=tweet["tweet_text"],
                    user_profile=tweet["profile"],
                    prompt_template=prompt_template
                )

            now_str = datetime.now().strftime("%Y/%m/%d %H:%M")
            acc_data = {
                "username": username,
                "profile": tweet["profile"],
                "profile_url": tweet["profile_url"],
                "timestamp": now_str,
                "flg_1": u("\\u30b9\\u30b3\\u30a2\\u5224\\u5b9a\\u6e08"),
                "flg_final": "TRUE" if follow_flag else "FALSE",
                "result": follow_result,
                "follow_recommended": "TRUE" if score_info["follow_recommended"] else "FALSE",
                "account_score": score_info["account_score"],
                "score_reason": score_info["reason"],
                "exclude_reason": score_info["exclude_reason"],
                "_skip_existing_check": True,
            }
            sheet_mgr.upsert_account(acc_data)

            if username not in registered_accounts:
                registered_accounts[username] = {"row_index": 0, "flg_final": ""}

            tw_data = {
                "username": username,
                "tweet_text": tweet["tweet_text"],
                "tweet_url": tweet["tweet_url"],
                "timestamp": now_str,
                "flg_1": "TRUE" if not score_info["excluded"] else "FALSE",
                "flg_2": "FALSE",
                "ai_comment": ai_comment,
                "like_result": like_result,
                "reply_result": "",
                "like_recommended": "TRUE" if score_info["like_recommended"] else "FALSE",
                "post_score": score_info["post_score"],
                "account_score": score_info["account_score"],
                "score_reason": score_info["reason"],
                "exclude_reason": score_info["exclude_reason"],
            }
            sheet_mgr.append_tweet(tw_data, "")

            processed_count += 1
            time.sleep(sleep_interval)

        logger.info("Batch finished")
        logger.info(f"processed={processed_count}, likes={like_count}, follows={follow_count}")

    except Exception as e:
        logger.error(f"Fatal error: {e}", exc_info=True)


if __name__ == "__main__":
    main()
