"""Daily 16:00 cron job — gather reply stats, call Claude, send 4 variant proposals to owner."""
import csv
import json
import os
import re
import sqlite3
import subprocess
from datetime import datetime
from pathlib import Path
from typing import Dict, List

BASE_DIR = Path(__file__).parent.parent
VARIANTS_FILE = BASE_DIR / "data/processed/aima_pending_variants.json"
STATE_FILE    = BASE_DIR / "data/processed/aima_bot_state.json"
DB_PATH       = BASE_DIR / "data/processed/aima_conversion_shadow.sqlite"
DEFAULT_ENV_FILES = [
    BASE_DIR / ".env",
    Path(r"C:\python_scripts\top_1_telegram_signals\.env"),
]
CLAUDE_BIN = os.environ.get("CLAUDE_BIN", "claude")


def load_env():
    for path in DEFAULT_ENV_FILES:
        if not path.exists():
            continue
        for raw in path.read_text(encoding="utf-8", errors="ignore").splitlines():
            line = raw.strip()
            if not line or line.startswith("#") or "=" not in line:
                continue
            k, v = line.split("=", 1)
            os.environ.setdefault(k.strip(), v.strip().strip('"').strip("'"))


def collect_stats():
    # type: () -> tuple
    by_hyp = {}        # type: Dict[str, Dict[str, int]]
    reply_samples = {} # type: Dict[str, List[str]]

    # Primary: SQLite aima_outreach_events (populated daily by aima_check_replies.py)
    if DB_PATH.exists():
        try:
            conn = sqlite3.connect(str(DB_PATH))
            conn.row_factory = sqlite3.Row
            cur = conn.cursor()
            cur.execute("""
                SELECT hypothesis,
                       COUNT(*) AS sent,
                       SUM(CASE WHEN outreach_status = 'replied' THEN 1 ELSE 0 END) AS replied
                FROM aima_outreach_events
                WHERE outreach_status IN ('sent', 'replied')
                  AND hypothesis IS NOT NULL AND hypothesis != ''
                GROUP BY hypothesis
            """)
            for row in cur.fetchall():
                by_hyp[row["hypothesis"]] = {"sent": row["sent"], "replied": row["replied"]}
            cur.execute("""
                SELECT hypothesis, reply_text FROM aima_outreach_events
                WHERE outreach_status = 'replied'
                  AND reply_text IS NOT NULL AND reply_text != ''
                ORDER BY replied_at_utc DESC
            """)
            for row in cur.fetchall():
                hyp = row["hypothesis"]
                samples = reply_samples.setdefault(hyp, [])
                if len(samples) < 5:
                    samples.append(row["reply_text"][:80])
            conn.close()
        except Exception as exc:
            print("[warn] SQLite read failed: {}".format(exc))
            by_hyp = {}

    # Fallback: CSV logs (if SQLite table is empty or missing)
    if not by_hyp:
        log_dir = BASE_DIR / "data/processed"
        for csv_path in sorted(log_dir.glob("aima_far_v*_telegram_log.csv")):
            with csv_path.open(encoding="utf-8-sig") as f:
                for row in csv.DictReader(f):
                    hyp = row.get("hypothesis", "unknown") or "unknown"
                    bucket = by_hyp.setdefault(hyp, {"sent": 0, "replied": 0})
                    if row.get("gate1_message_sent", "").lower() == "yes":
                        bucket["sent"] += 1
                    if row.get("replied", "").lower() == "yes":
                        bucket["replied"] += 1
                        text = (row.get("reply_text") or "").strip()
                        if text:
                            samples = reply_samples.setdefault(hyp, [])
                            if len(samples) < 5:
                                samples.append(text)
        if by_hyp:
            print("[stats] loaded from CSV (SQLite not yet populated)")

    return by_hyp, reply_samples


def find_champion(by_hyp):
    # type: (Dict[str, Dict[str, int]]) -> tuple
    """Return (hyp_name, rate, text) for the best hypothesis (min 5 sent), or None."""
    best_hyp, best_rate = None, -1.0
    for hyp, s in by_hyp.items():
        sent = s.get("sent", 0)
        if sent < 5:
            continue
        rate = s.get("replied", 0) / sent
        if rate > best_rate:
            best_rate, best_hyp = rate, hyp
    if best_hyp is None:
        return None
    # Find the gate1_text from contacts CSVs
    proc_dir = BASE_DIR / "data/processed"
    for csv_path in sorted(proc_dir.glob("aima_far_v*_contacts.csv"), reverse=True):
        with csv_path.open(encoding="utf-8-sig") as f:
            for row in csv.DictReader(f):
                if row.get("hypothesis") == best_hyp:
                    text = (row.get("gate1_text") or "").strip()
                    if text:
                        return (best_hyp, best_rate, text)
    return None


def format_stats(by_hyp):
    # type: (Dict[str, Dict[str, int]]) -> str
    if not by_hyp:
        return "Даних ще немає."
    lines = []
    for hyp, s in sorted(by_hyp.items()):
        sent, replied = s["sent"], s["replied"]
        rate = "{:.0f}%".format(replied / sent * 100) if sent else "—"
        lines.append("  {}: sent={} replied={} rate={}".format(hyp, sent, replied, rate))
    return "\n".join(lines)


def call_claude(prompt):
    # type: (str) -> str
    result = subprocess.run(
        [CLAUDE_BIN, "--print"],
        input=prompt,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        encoding="utf-8",
        errors="replace",
        timeout=120,
    )
    if result.returncode != 0:
        raise RuntimeError("claude exit {}: {}".format(result.returncode, result.stderr[:300]))
    return result.stdout.strip()


def parse_variants(text):
    # type: (str) -> List[dict]
    variants = []
    blocks = re.split(r'\n(?=\s*(?:\*\*)?[A-D][.\)]\s)', text)
    if len(blocks) < 2:
        blocks = [b.strip() for b in text.split("\n\n") if b.strip()]

    for block in blocks:
        label_m = re.match(r'^\s*\*{0,2}([A-D])[.\):]\*{0,2}\s*', block)
        if not label_m:
            continue  # skip preamble / separator lines
        label = label_m.group(1)

        # Why: may have **Why:** or Why: format
        why_m = re.search(r'\*{0,2}Why:\*{0,2}\s*(.+?)(?:\n|$)', block, re.IGNORECASE)
        why = why_m.group(1).strip().strip("*").strip() if why_m else ""

        # Prefer > blockquote as message text
        quote_m = re.search(r'>\s*(.+?)(?=\n\n|\n\*{0,2}Why:|\Z)', block, re.DOTALL | re.IGNORECASE)
        if quote_m:
            msg = re.sub(r'\s+', ' ', quote_m.group(1)).strip()
        else:
            msg_end = why_m.start() if why_m else len(block)
            msg = block[label_m.end():msg_end]
            # drop section heading line (e.g. "** Конкретна цифра — ...\n")
            msg = re.sub(r'^\*+[^\n]+\*{0,2}\n', '', msg).strip()
            msg = re.sub(r'^\*{2,}', '', msg).strip()

        if msg:
            variants.append({"label": label, "text": msg[:200], "why": why})
        if len(variants) == 4:
            break

    return variants


def send_telegram(token, chat_id, text):
    import requests
    r = requests.post(
        "https://api.telegram.org/bot{}/sendMessage".format(token),
        json={"chat_id": chat_id, "text": text, "parse_mode": "HTML"},
        timeout=30,
    )
    return r.json()


def main():
    load_env()
    token = os.environ.get("AIMA_BOT_TOKEN")
    if not token:
        raise SystemExit("AIMA_BOT_TOKEN missing")
    env_mode  = os.environ.get("AIMA_ENV", "").lower()
    env_label = "[🧪 local] " if env_mode in ("test", "local") else ""

    state = {}
    if STATE_FILE.exists():
        state = json.loads(STATE_FILE.read_text(encoding="utf-8"))
    owner_chat_id = state.get("owner_chat_id")
    if not owner_chat_id:
        print("[warn] owner_chat_id not set — owner should send /myid to bot first")

    by_hyp, reply_samples = collect_stats()
    stats_text = format_stats(by_hyp)
    print("[stats]\n{}".format(stats_text))

    # Previous variants for context
    prev_variants = []  # type: List[dict]
    if VARIANTS_FILE.exists():
        try:
            prev_data = json.loads(VARIANTS_FILE.read_text(encoding="utf-8"))
            prev_variants = prev_data.get("variants", [])
        except Exception:
            pass

    prev_context = ""
    if prev_variants:
        prev_context = "Попередні гіпотези:\n" + "\n".join(
            "  {}: {}".format(v["label"], v["text"][:100]) for v in prev_variants
        )

    replies_ctx = ""
    if reply_samples:
        lines = ["Реальні відповіді клієнтів з поточного батчу:"]
        for hyp, texts in sorted(reply_samples.items()):
            for t in texts[:4]:
                lines.append('  [{}] "{}"'.format(hyp[:40], t[:120]))
        replies_ctx = "\n".join(lines)

    # Champion: best hypothesis with >= 5 sent — keep it, generate 3 competitors
    champion = find_champion(by_hyp)
    if champion:
        champ_hyp, champ_rate, champ_text = champion
        print("[champion] {} rate={:.0f}%: {}".format(champ_hyp, champ_rate * 100, champ_text[:60]))
        champion_block = (
            "Гіпотеза-ЧЕМПІОН (НЕ змінюй її — вона вже довела ефективність, rate={:.0f}%):\n"
            "A. {}\n\n"
            "Запропонуй лише 3 НОВІ конкуренти B, C, D.\n"
        ).format(champ_rate * 100, champ_text)
        new_format = (
            "B. [текст повідомлення]\n"
            "Why: [обґрунтування]\n\n"
            "C. [текст повідомлення]\n"
            "Why: [обґрунтування]\n\n"
            "D. [текст повідомлення]\n"
            "Why: [обґрунтування]"
        )
    else:
        champion_block = ""
        new_format = (
            "A. [текст повідомлення]\n"
            "Why: [обґрунтування]\n\n"
            "B. [текст повідомлення]\n"
            "Why: [обґрунтування]\n\n"
            "C. [текст повідомлення]\n"
            "Why: [обґрунтування]\n\n"
            "D. [текст повідомлення]\n"
            "Why: [обґрунтування]"
        )

    n_new = 3 if champion else 4
    prompt = (
        "Ти маркетолог для AIMA — платформи для створення інтернет-магазинів в Україні.\n"
        "Проаналізуй статистику і реальні відповіді клієнтів, запропонуй {n} нові гіпотези "
        "для наступної хвилі cold outreach у Telegram.\n\n"
        "Поточна статистика відповідей по гіпотезах:\n{stats}\n\n"
        "{replies}\n\n"
        "{champion}"
        "{prev}\n"
        "Цільова аудиторія: люди, що завантажили додаток AIMA, але ще не створили магазин.\n"
        "Контекст: B2C SaaS, Україна 2025-2026, ринок e-commerce, малий бізнес.\n\n"
        "Для кожної нової гіпотези напиши:\n"
        "- Коротке повідомлення для Telegram (до 160 символів, від імені Насті з AIMA)\n"
        "- Why: одне речення — обґрунтуй через аналіз відповідей клієнтів, "
        "best practices cold outreach або success stories з B2C SaaS\n\n"
        "ФОРМАТ ВІДПОВІДІ — СУВОРО ЦЕЙ, без зірочок, заголовків, блоків цитат:\n"
        "{fmt}"
    ).format(
        n=n_new,
        stats=stats_text,
        replies=replies_ctx,
        champion=champion_block,
        prev=prev_context,
        fmt=new_format,
    )

    claude_text = None
    try:
        claude_text = call_claude(prompt)
        print("[claude] ok ({} chars)".format(len(claude_text)))
    except Exception as exc:
        print("[claude] FAILED: {}".format(exc))

    if claude_text:
        new_variants = parse_variants(claude_text)
        if champion and new_variants:
            champ_hyp, champ_rate, champ_text = champion
            champion_variant = {
                "label": "A",
                "text": champ_text,
                "why": "Гіпотеза-чемпіон — збережено без змін (rate={:.0f}%)".format(champ_rate * 100),
            }
            # Re-label parsed variants B/C/D if they started from A
            relabeled = []
            next_label = ord("B")
            for v in new_variants:
                if v["label"] == "A":
                    v = dict(v, label=chr(next_label))
                relabeled.append(v)
                next_label = ord(v["label"]) + 1
            variants = [champion_variant] + relabeled[:3]
        else:
            variants = new_variants
        status = "pending_choice"
        raw = claude_text
    elif prev_variants:
        variants = prev_variants
        status = "fallback_kept_previous"
        raw = "claude failed; kept previous"
    else:
        variants = [
            {"label": "A", "text": "Привіт, це Настя з AIMA. Хотіла коротко уточнити: для вас ще актуальна ідея власного інтернет-магазину?", "why": "перевірений FAR_A — низький тиск, відкрите питання"},
            {"label": "B", "text": "Привіт, це Настя з AIMA. Ви реєструвались у нас, але магазин не створили. Підкажіть, ви тоді хотіли зробити власний інтернет-магазин чи просто тестували додаток?", "why": "перевірений FAR_B — прив'язка до наміру"},
            {"label": "C", "text": "Привіт, це Настя з AIMA. Хотіла коротко уточнити: для вас ще актуальна ідея власного інтернет-магазину, де будуть тільки ваші товари?", "why": "ефект власності — тільки ваші товари"},
            {"label": "D", "text": "Привіт, це Настя з AIMA. Хотіла коротко уточнити: вам було б корисно мати одне посилання, де клієнт бачить усі ваші товари?", "why": "зручність — одне посилання для покупця"},
        ]
        status = "fallback_defaults"
        raw = "claude failed; using defaults"

    payload = {
        "generated_at": datetime.now().isoformat(),
        "status": status,
        "chosen": [],
        "variants": variants,
        "raw_claude_response": raw,
        "stats": by_hyp,
        "reply_samples": reply_samples,
    }
    VARIANTS_FILE.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
    print("[saved] {}".format(VARIANTS_FILE.name))

    if owner_chat_id and token:
        msg = (
            "\U0001f916 <b>{}AIMA — варіанти повідомлень на завтра</b>\n"
            "<i>{}</i>\n\n"
        ).format(env_label, datetime.now().strftime("%d.%m %H:%M"))
        for v in variants:
            msg += "<b>{}.</b> {}\n".format(v["label"], v["text"])
            if v.get("why"):
                msg += "   <i>Why: {}</i>\n".format(v["why"])
            msg += "\n"
        if status.startswith("fallback"):
            msg += "⚠️ <i>Агент не зміг згенерувати нові варіанти — використано попередні.</i>\n\n"
        msg += "Оберіть два варіанти: /pick AB, /pick AC тощо\nДо 16:30 — інакше оберу сам."
        result = send_telegram(token, owner_chat_id, msg)
        if result.get("ok"):
            print("[sent] variants to owner {}".format(owner_chat_id))
        else:
            print("[warn] telegram send failed: {}".format(result))
    else:
        print("[skip] owner_chat_id not set, not sending")


if __name__ == "__main__":
    main()
