#!/usr/bin/env python3
"""
Turbo Twitter Bot Marketing System — Tweet Generator

Generates daily tweets for 5 bot personas, each with their own specialty
but rotating through motivational, technical, and educational styles.

Usage:
    python bots.py generate   # Generate today's tweets (70% chance per bot)
    python bots.py preview    # Show all pending tweets
"""

import argparse
import random
import sqlite3
import sys
from datetime import datetime, timezone
from pathlib import Path

import yaml


# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------

BASE_DIR = Path(__file__).resolve().parent
CONFIG_PATH = BASE_DIR / "config.yaml"
DB_PATH = BASE_DIR / "tweets.db"


def load_config():
    with open(CONFIG_PATH, "r") as f:
        return yaml.safe_load(f)


# ---------------------------------------------------------------------------
# Database helpers
# ---------------------------------------------------------------------------

def init_db(db_path=None):
    """Create the tweets table if it doesn't exist."""
    conn = sqlite3.connect(str(db_path or DB_PATH))
    conn.execute("""
        CREATE TABLE IF NOT EXISTS tweets (
            id          INTEGER PRIMARY KEY AUTOINCREMENT,
            persona     TEXT    NOT NULL,
            content     TEXT    NOT NULL,
            style       TEXT    NOT NULL,
            mentions_turbo INTEGER NOT NULL DEFAULT 0,
            created_at  TEXT    NOT NULL,
            posted_at   TEXT,
            status      TEXT    NOT NULL DEFAULT 'pending'
        )
    """)
    conn.commit()
    return conn


def insert_tweet(conn, persona, content, style, mentions_turbo):
    now = datetime.now(timezone.utc).isoformat()
    conn.execute(
        "INSERT INTO tweets (persona, content, style, mentions_turbo, created_at, status) "
        "VALUES (?, ?, ?, ?, ?, 'pending')",
        (persona, content, style, int(mentions_turbo), now),
    )
    conn.commit()


def get_pending_tweets(conn):
    cur = conn.execute(
        "SELECT id, persona, content, style, mentions_turbo, created_at "
        "FROM tweets WHERE status = 'pending' ORDER BY created_at"
    )
    return cur.fetchall()


# ---------------------------------------------------------------------------
# Random fill helpers
# ---------------------------------------------------------------------------

def rand_btc_price():
    return f"${random.randint(58, 105)},{random.randint(100, 999)}"


def rand_pct():
    return f"{random.choice([0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.5, 1.0, 1.5, 2.0])}%"


def rand_small_pct():
    return f"{random.choice([0.05, 0.08, 0.1, 0.12, 0.15, 0.2])}%"


def rand_win_rate():
    return f"{random.randint(54, 68)}%"


def rand_timeframe():
    return random.choice(["5-min", "5-minute", "five-minute"])


def rand_seconds():
    return str(random.choice([30, 45, 60, 90, 120]))


def rand_risk_pct():
    return random.choice(["1%", "2%", "3%", "1-2%", "2-3%"])


def rand_strategy():
    return random.choice([
        "mean-reversion", "momentum", "breakout", "range-bound",
        "volatility-fade", "trend-following", "VWAP crossover",
    ])


def rand_indicator():
    return random.choice([
        "RSI", "MACD", "Bollinger Bands", "VWAP", "EMA",
        "volume profile", "order flow", "OBV",
    ])


def rand_hashtag(persona_key, config):
    tags = config["personas"][persona_key]["hashtags"]
    return random.choice(tags)


# ---------------------------------------------------------------------------
# Turbo mention fragments (woven in naturally)
# ---------------------------------------------------------------------------

TURBO_MENTIONS = [
    "Been testing this on Turbo (turbo.luckyst.trade) — 5-min BTC prediction markets on Monad.",
    "Turbo's 5-min BTC markets are a good sandbox for this kind of thinking.",
    "If you want to practice, Turbo (turbo.luckyst.trade) runs 5-min BTC prediction rounds on Monad.",
    "I've been sharpening my edge on Turbo — 5-min prediction markets, no leverage, pure reads.",
    "This is exactly the kind of edge that works on Turbo's 5-min BTC markets.",
    "Turbo on Monad does this well — 5-min rounds, binary outcome, clean signal.",
    "Markets like Turbo (turbo.luckyst.trade) strip trading down to its core: will BTC be higher in 5 minutes?",
    "Turbo's prediction market format forces you to think in probabilities, not narratives.",
    "Platforms like Turbo (turbo.luckyst.trade) make this accessible — 5-min BTC calls on Monad.",
    "Been using Turbo to test short-timeframe reads. Clean format, fast feedback.",
]


def maybe_append_turbo(tweet, config):
    """With configured probability, append a Turbo mention to the tweet."""
    prob = config["turbo"]["mention_probability"]
    if random.random() < prob:
        mention = random.choice(TURBO_MENTIONS)
        combined = f"{tweet} {mention}"
        # If it fits in 280 chars, use it; otherwise skip the mention
        if len(combined) <= 280:
            return combined, True
    return tweet, False


# ---------------------------------------------------------------------------
# Tweet template banks — per persona, per style
# Each template is a callable that returns a string.
# ---------------------------------------------------------------------------

def _templates_turbo_alpha():
    """@TurboAlpha — Inspirational trading quotes, mindset posts."""
    return {
        "motivational": [
            lambda: "The market doesn't care about your feelings. It cares about your edge. Find yours in 5 minutes.",
            lambda: "Discipline is choosing between what you want now and what you want most. In trading, that choice happens every candle.",
            lambda: "You don't need to trade every market. You need to trade the right ones well.",
            lambda: "Consistency beats intensity. One good read per day compounds faster than ten reckless bets.",
            lambda: "The best traders I know aren't the smartest. They're the most patient.",
            lambda: "Your P&L is a lagging indicator. Your process is the leading one.",
            lambda: f"Lost {rand_pct()} today. Doesn't matter. The process was sound. That's all I control.",
            lambda: "Stop chasing perfect entries. Start building a repeatable edge.",
            lambda: "Every losing trade is tuition — but only if you actually study the lesson.",
            lambda: "Markets reward the prepared and punish the reactive. Which one are you today?",
            lambda: "Conviction without evidence is just gambling. Build your thesis, then act.",
            lambda: "The difference between a good trader and a great one is what they do when nothing is happening.",
        ],
        "technical": [
            lambda: f"Watching BTC at {rand_btc_price()}. Key level. How you handle inflection points defines your edge.",
            lambda: f"When {rand_indicator()} diverges from price on a {rand_timeframe()} chart, pay attention. The market is telling you something.",
            lambda: f"A {rand_strategy()} setup only works when you respect the stop. Edge without risk management is just hope.",
            lambda: f"BTC moved {rand_pct()} in the last candle. Most people reacted. The edge belongs to those who anticipated.",
            lambda: "Price action over indicators. Indicators confirm what price already told you.",
            lambda: f"Studied 200 {rand_timeframe()} candles this week. Pattern: the first {rand_seconds()}s set the tone for the whole window.",
        ],
        "educational": [
            lambda: "Edge is just a statistical advantage applied repeatedly. Nothing more, nothing less. Treat it that way.",
            lambda: "Three things that separate consistent traders: position sizing, emotional control, and knowing when to sit out.",
            lambda: "A common mistake: confusing a winning streak with skill. Sample size matters. Track at least 100 trades before drawing conclusions.",
            lambda: f"Prediction markets strip trading to its essence. Will price go up or down in the next {rand_timeframe()} window? That clarity is underrated.",
            lambda: "If you can't explain your edge in one sentence, you probably don't have one yet.",
            lambda: "Journaling your trades is the single highest-ROI habit in trading. Do it for 30 days and you'll see.",
            lambda: "Risk-reward isn't just a ratio. It's a mindset. Every position you take should have a clear thesis and a clear exit.",
        ],
    }


def _templates_five_min_edge():
    """@5MinEdge — Trading tips, strategy advice, risk management."""
    return {
        "motivational": [
            lambda: "Small edges, applied consistently, build real returns. Don't chase the big score.",
            lambda: f"Risked {rand_risk_pct()} per trade this month. Up 8% overall. Boring works.",
            lambda: "The best trade you'll ever make is the one you didn't take because the setup wasn't there.",
            lambda: "Stop looking for the holy grail strategy. Start looking for the strategy you can actually stick to.",
            lambda: "Patience is a position. Sometimes the highest-EV move is doing nothing.",
            lambda: "One clean trade per day beats five mediocre ones. Quality over quantity, always.",
        ],
        "technical": [
            lambda: f"Risk management tip: Never risk more than {rand_risk_pct()} on a single {rand_timeframe()} market. Small bets, consistent edge.",
            lambda: f"Quick {rand_strategy()} setup: wait for {rand_indicator()} to signal, confirm with volume, enter in the first {rand_seconds()}s. Keep stops tight.",
            lambda: f"Tested a {rand_strategy()} approach on {rand_timeframe()} BTC data. Win rate: {rand_win_rate()} over 150 samples. Edge is real but sizing matters.",
            lambda: f"When BTC is ranging within {rand_small_pct()}, {rand_timeframe()} markets become coin flips. Save your capital for high-conviction setups.",
            lambda: f"Pro tip: {rand_indicator()} on the 1-min chart can front-run {rand_timeframe()} moves. Not always, but enough to matter.",
            lambda: f"Tracking {rand_indicator()} divergence before each {rand_timeframe()} window. When it lines up with volume, the hit rate jumps.",
            lambda: f"Best setups this week: {rand_strategy()} entries within the first {rand_seconds()}s of the window. The late entries got chopped.",
            lambda: f"A {rand_risk_pct()} risk limit per trade sounds conservative until you realize it's what lets you survive 20 losers in a row.",
        ],
        "educational": [
            lambda: "Position sizing 101: Your edge means nothing if one bad trade wipes out ten good ones. Size accordingly.",
            lambda: f"What is {rand_indicator()}? It measures momentum and helps you time entries. Not a crystal ball, but a useful filter for {rand_timeframe()} trades.",
            lambda: "The Kelly criterion tells you how much to bet given your edge. Most traders bet 2-5x too much. That's why they blow up.",
            lambda: "Win rate vs. risk-reward: You can be profitable at 40% win rate if your winners are 3x your losers. Math over feelings.",
            lambda: f"Why {rand_timeframe()} markets? Shorter timeframes mean faster feedback loops. You learn more in a week than most learn in a month.",
            lambda: "A stop-loss isn't an admission of failure. It's the cost of staying in the game.",
            lambda: "Three questions before every trade: What's my edge? What's my risk? What's my exit? If you can't answer all three, skip it.",
        ],
    }


def _templates_algo_turbo():
    """@AlgoTurbo — Trading bot ideas, algo strategies, automation."""
    return {
        "motivational": [
            lambda: "Spent 6 hours backtesting a strategy that didn't work. Not wasted time — I eliminated one more wrong answer.",
            lambda: "Automate your edge, not your emotions. The bot doesn't flinch. That's the point.",
            lambda: "The best algo traders aren't coders first. They're market thinkers who happen to code.",
            lambda: "Every manual trade you take is data you could be feeding a model. Start logging everything.",
            lambda: "My first bot lost money for 3 weeks straight. My current bot has been profitable for 4 months. Iteration is the strategy.",
            lambda: "Discipline is hard to maintain manually. That's literally why bots exist.",
        ],
        "technical": [
            lambda: f"Built a {rand_strategy()} bot for {rand_timeframe()} BTC markets. Buys YES when price dips {rand_small_pct()} below strike in first {rand_seconds()}s. {rand_win_rate()} win rate so far.",
            lambda: f"Testing a new signal: {rand_indicator()} crossover on the 1-min chart, filtered by volume. Triggers about 3 trades per day on {rand_timeframe()} windows.",
            lambda: f"Bot update: added a volatility filter. Skips {rand_timeframe()} windows where ATR is below threshold. Win rate improved by 4%.",
            lambda: f"Simple algo: if BTC moves more than {rand_small_pct()} in the first {rand_seconds()}s of a {rand_timeframe()} window, bet on continuation. Backtests well.",
            lambda: f"Running two bots in parallel: {rand_strategy()} and {rand_strategy()}. Comparing Sharpe ratios over 200 trades each.",
            lambda: f"Latency matters. Reduced my execution time from 800ms to 200ms. Slippage dropped, and {rand_strategy()} entries improved noticeably.",
            lambda: f"Feature engineering for {rand_timeframe()} prediction: lagged returns, {rand_indicator()}, spread, and order book imbalance. Four features, decent edge.",
            lambda: "Overfitting is the silent killer of algo strategies. If your backtest looks too good, it probably is. Walk-forward test everything.",
        ],
        "educational": [
            lambda: "What is a trading bot? Software that executes a predefined strategy automatically. No emotions, no hesitation, no sleep.",
            lambda: f"Backtesting 101: Test your {rand_strategy()} idea on historical data before risking real capital. Minimum 100 trades for statistical significance.",
            lambda: "Paper trading vs. live trading: paper trades don't account for slippage, latency, or the psychological difference of real money on the line.",
            lambda: "The simplest profitable bot I've built: compare current price to 5-min moving average. If below, bet YES. If above, bet NO. That's it.",
            lambda: "Three parts of any algo strategy: signal generation, position sizing, execution. Most people focus only on signals. That's why most bots fail.",
            lambda: f"Walk-forward optimization: train your model on 1000 {rand_timeframe()} windows, test on the next 200. Repeat. This prevents overfitting better than anything else.",
            lambda: "You don't need ML to build a profitable bot. Simple rules, properly sized, with disciplined execution beat complex models more often than you'd think.",
        ],
    }


def _templates_btc_pulse():
    """@BTCPulse5m — BTC market commentary, 5-min market calls."""
    return {
        "motivational": [
            lambda: "Sat out the last three windows. No shame in that. The market will be here tomorrow.",
            lambda: f"BTC at {rand_btc_price()} and everyone has an opinion. I'll wait for the chart to have one.",
            lambda: "Took a loss on that last call. It happens. The edge is in the aggregate, not any single trade.",
            lambda: "Three wins in a row. Tempting to size up. Not going to. That's how you give it all back.",
            lambda: "Boring markets make patient traders. Volatile markets reward them.",
            lambda: "Missed a clean setup. It stings, but chasing it would sting worse.",
        ],
        "technical": [
            lambda: f"BTC hovering at {rand_btc_price()}. Key resistance above. Next {rand_timeframe()} candle could break either way. I'm sitting this one out.",
            lambda: f"BTC just broke {rand_btc_price()} with volume. {rand_indicator()} confirms. Leaning bullish for the next {rand_timeframe()} window.",
            lambda: f"Flat price action around {rand_btc_price()}. {rand_indicator()} is neutral. No edge here — skipping this round.",
            lambda: f"BTC dropped {rand_small_pct()} in the last 60 seconds. Watching for a bounce at {rand_btc_price()}. If it holds, next {rand_timeframe()} window looks like YES.",
            lambda: f"Volume spike at {rand_btc_price()}. Someone knows something, or someone is getting liquidated. Either way, {rand_indicator()} is flashing.",
            lambda: f"BTC range-bound between {rand_btc_price()} and {rand_btc_price()}. Tight range means low conviction. Sitting out until it breaks.",
            lambda: f"Clean {rand_strategy()} setup on BTC right now. {rand_indicator()} aligning with price action. Taking a small position on the next {rand_timeframe()} window.",
            lambda: f"Post-move analysis: BTC ripped {rand_pct()} in that last window. The signal was {rand_indicator()} divergence 2 minutes before. Adding that to the playbook.",
        ],
        "educational": [
            lambda: f"Reading a {rand_timeframe()} chart: focus on the open and the first {rand_seconds()}s. That initial move often telegraphs the rest of the candle.",
            lambda: "Liquidity matters more than direction. A thin order book at resistance means a breakout can happen fast and hard.",
            lambda: f"What drives BTC in a {rand_timeframe()} window? Mostly: existing momentum, order book depth, and any macro news in the last 30 minutes.",
            lambda: "Most BTC moves in a 5-min window are noise. The skill is distinguishing noise from signal. That takes screen time.",
            lambda: f"Support and resistance on a {rand_timeframe()} chart are different from daily levels. They're less about price memory and more about order clustering.",
            lambda: "Volume precedes price. If you see a volume spike without a corresponding price move, something is loading up. Watch the next candle closely.",
            lambda: f"Why I track {rand_indicator()} for {rand_timeframe()} BTC trades: it filters out low-conviction setups and keeps me in only when conditions align.",
        ],
    }


def _templates_predictoor_dao():
    """@PredictoorDAO — Prediction market education, DeFi explainers."""
    return {
        "motivational": [
            lambda: "Prediction markets are the most honest form of market research. People put money where their mouth is.",
            lambda: "You don't need a Bloomberg terminal to have an edge. You need a clear thesis and the discipline to test it.",
            lambda: "DeFi isn't about replacing finance. It's about making it accessible to anyone with an internet connection.",
            lambda: "The barrier to trading used to be a brokerage account and $25k. Now it's a wallet and a thesis.",
            lambda: "Prediction markets will eventually be how we settle every empirical question. We're early.",
            lambda: "The best part of on-chain markets: the rules are in the code. No hidden terms, no counterparty risk, no fine print.",
        ],
        "technical": [
            lambda: "Prediction markets are the purest form of price discovery. No leverage, no liquidation. Just: will BTC be higher in 5 minutes?",
            lambda: f"Binary outcomes simplify everything. In a {rand_timeframe()} prediction market, your edge comes down to one question: direction. Focus there.",
            lambda: "On-chain settlement removes counterparty risk. Your trade resolves by code, not by someone's decision to honor it.",
            lambda: "AMMs for prediction markets work differently than for token swaps. The pricing reflects collective probability estimates, not liquidity ratios.",
            lambda: f"Interesting dynamic: {rand_timeframe()} prediction markets have tighter feedback loops than options. You know if you were right in minutes, not days.",
            lambda: "Smart contract settlement means no disputes. The oracle reports, the contract pays. Simple as that.",
            lambda: "Prediction market mechanics: buy YES if you think the event happens, buy NO if you don't. Price reflects the market's probability estimate.",
        ],
        "educational": [
            lambda: "What is a prediction market? A market where you trade on the outcome of a future event. Prices reflect the crowd's probability estimate.",
            lambda: "Prediction markets vs. polls: markets have skin in the game. Polls don't. That's why markets tend to be more accurate.",
            lambda: f"How {rand_timeframe()} BTC prediction markets work: at the start of each window, you bet YES (price goes up) or NO (price goes down). Settled automatically.",
            lambda: "DeFi prediction markets remove the middleman. No bookmaker taking a cut. Just traders, a contract, and an oracle.",
            lambda: "What is an oracle? In DeFi, it's a data feed that brings real-world information on-chain. For BTC prediction markets, it reports the price.",
            lambda: "Binary markets are the simplest way to express a directional view. Up or down. Yes or no. That simplicity is a feature, not a limitation.",
            lambda: "The efficient market hypothesis says prices reflect all available information. Prediction markets test that idea in real time, every 5 minutes.",
            lambda: "Why Monad? High throughput and low fees make frequent prediction rounds viable. You can't run 5-min markets on a chain that takes 30s per block.",
        ],
    }


# Map persona keys to template generators
TEMPLATE_REGISTRY = {
    "turbo_alpha": _templates_turbo_alpha,
    "five_min_edge": _templates_five_min_edge,
    "algo_turbo": _templates_algo_turbo,
    "btc_pulse": _templates_btc_pulse,
    "predictoor_dao": _templates_predictoor_dao,
}


# ---------------------------------------------------------------------------
# Tweet generation
# ---------------------------------------------------------------------------

def generate_tweet(persona_key, config):
    """Generate a single tweet for the given persona.

    Returns (content, style, mentions_turbo) or None if something goes wrong.
    """
    templates_by_style = TEMPLATE_REGISTRY[persona_key]()
    styles = list(templates_by_style.keys())
    style = random.choice(styles)
    template_fn = random.choice(templates_by_style[style])
    tweet = template_fn()

    # Maybe add a hashtag (50% chance)
    if random.random() < 0.5:
        tag = rand_hashtag(persona_key, config)
        candidate = f"{tweet} {tag}"
        if len(candidate) <= 280:
            tweet = candidate

    # Maybe mention Turbo (10-20% configured probability)
    tweet, mentions_turbo = maybe_append_turbo(tweet, config)

    # Final length check — truncate is a last resort (shouldn't happen with
    # well-crafted templates, but safety first)
    if len(tweet) > 280:
        tweet = tweet[:277] + "..."

    return tweet, style, mentions_turbo


def generate_daily(config=None):
    """Roll the dice for each persona and generate today's tweets.

    Returns a list of (persona, content, style, mentions_turbo) tuples.
    """
    if config is None:
        config = load_config()

    conn = init_db()
    generated = []
    daily_prob = config["posting"]["daily_post_probability"]
    min_tweets = config["posting"]["tweets_per_day"]["min"]
    max_tweets = config["posting"]["tweets_per_day"]["max"]

    for persona_key in config["personas"]:
        handle = config["personas"][persona_key]["handle"]

        # 70% chance this bot posts today
        if random.random() > daily_prob:
            print(f"  {handle} — skipped today (dice roll)")
            continue

        num_tweets = random.randint(min_tweets, max_tweets)
        print(f"  {handle} — generating {num_tweets} tweet(s)")

        for _ in range(num_tweets):
            content, style, mentions_turbo = generate_tweet(persona_key, config)
            insert_tweet(conn, persona_key, content, style, mentions_turbo)
            generated.append((persona_key, content, style, mentions_turbo))

    conn.close()
    return generated


def preview_pending():
    """Print all pending tweets from the database."""
    conn = init_db()
    rows = get_pending_tweets(conn)
    conn.close()

    if not rows:
        print("No pending tweets.")
        return

    print(f"\n{'='*60}")
    print(f"  PENDING TWEETS ({len(rows)} total)")
    print(f"{'='*60}\n")

    for row in rows:
        tweet_id, persona, content, style, mentions_turbo, created_at = row
        turbo_flag = " [TURBO]" if mentions_turbo else ""
        print(f"  #{tweet_id} | {persona} | {style}{turbo_flag}")
        print(f"  Created: {created_at}")
        print(f"  {content}")
        print(f"  ({len(content)} chars)")
        print(f"  {'-'*56}")


# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------

def main():
    parser = argparse.ArgumentParser(
        description="Turbo Twitter Bot — Tweet Generator"
    )
    parser.add_argument(
        "command",
        choices=["generate", "preview"],
        help="'generate' to create today's tweets, 'preview' to view pending tweets",
    )
    args = parser.parse_args()

    if args.command == "generate":
        config = load_config()
        print("\nGenerating daily tweets...\n")
        generated = generate_daily(config)
        print(f"\nDone. Generated {len(generated)} tweet(s).\n")
        # Show what was generated
        for persona, content, style, mentions_turbo in generated:
            turbo_flag = " [TURBO]" if mentions_turbo else ""
            print(f"  [{persona}] ({style}{turbo_flag})")
            print(f"  {content}")
            print(f"  ({len(content)} chars)\n")

    elif args.command == "preview":
        preview_pending()


if __name__ == "__main__":
    main()
