#!/usr/bin/env python3
"""
GHL Composable Module — Review Generator
==========================================
After job completion webhook from GHL:
1. Sends personalised SMS requesting Google review
2. Sends optional follow-up email
3. Tracks response rate in PostgreSQL
4. Schedules one follow-up SMS after 48h if no click recorded

This is the COMPOSABLE MODULE (business logic layer).
For the GHL API deployment script (creating workflows, custom fields),
see GHL_MODULES/review_generator/ghl_api_deploy.py

Usage (standalone):
    python E:\\genesis-system\\GHL\\modules\\review_generator.py \\
        --contact-id "abc123" \\
        --first-name "Michael" \\
        --technician "Jake" \\
        --job-type "Hot water replacement" \\
        --phone "+61412345678" \\
        --google-review-link "https://g.page/r/XXXXX/review" \\
        --business-name "Precision Plumbing" \\
        --ghl-location-id "loc123"

Usage (webhook mode — receives GHL webhook JSON on stdin):
    echo '{"contact":{"id":"abc","phone":"+61412..."},...}' | \\
        python review_generator.py --webhook-mode

Author: Genesis Systems Builder
Created: 2026-02-23
"""

from __future__ import annotations

import argparse
import hashlib
import json
import logging
import os
import sys
import time
from datetime import datetime, timedelta
from pathlib import Path
from typing import Any, Dict, Optional

# ── Path setup ────────────────────────────────────────────────────────────────
GENESIS_ROOT = Path(r"E:\genesis-system")
sys.path.insert(0, str(GENESIS_ROOT / "data" / "genesis-memory"))
sys.path.insert(0, str(GENESIS_ROOT))

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(name)s — %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)
logger = logging.getLogger("ReviewGenerator")

# ── GHL API config ─────────────────────────────────────────────────────────────
GHL_API_BASE = "https://services.leadconnectorhq.com"
GHL_API_VERSION = "2021-07-28"

# ── SMS templates ──────────────────────────────────────────────────────────────
REVIEW_SMS_TEMPLATE = (
    "Hi {first_name}, thanks for choosing {business_name}. "
    "We hope {technician} took great care of you today. "
    "If you have a moment, a Google review means the world to us: {review_link}"
)

REVIEW_FOLLOWUP_SMS_TEMPLATE = (
    "Hi {first_name}, just following up — if you had a good experience with {business_name}, "
    "we'd love a quick review: {review_link}"
)

# ── PostgreSQL DDL ─────────────────────────────────────────────────────────────
CREATE_REVIEW_REQUESTS_TABLE = """
CREATE TABLE IF NOT EXISTS review_requests (
    id SERIAL PRIMARY KEY,
    request_id TEXT UNIQUE NOT NULL,
    contact_id TEXT NOT NULL,
    location_id TEXT NOT NULL,
    first_name TEXT,
    technician TEXT,
    job_type TEXT,
    phone TEXT,
    review_link TEXT,
    business_name TEXT,
    sms_sent_at TIMESTAMPTZ,
    followup_sent_at TIMESTAMPTZ,
    clicked_at TIMESTAMPTZ,
    status TEXT DEFAULT 'pending',
    created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_rr_contact ON review_requests(contact_id);
CREATE INDEX IF NOT EXISTS idx_rr_location ON review_requests(location_id);
CREATE INDEX IF NOT EXISTS idx_rr_status ON review_requests(status);
CREATE INDEX IF NOT EXISTS idx_rr_sms_sent ON review_requests(sms_sent_at);
"""


# ── Database ───────────────────────────────────────────────────────────────────
def get_pg_connection():
    try:
        from elestio_config import PostgresConfig
        import psycopg2
        return psycopg2.connect(**PostgresConfig.get_connection_params())
    except ImportError:
        import psycopg2
        return psycopg2.connect(
            host=os.environ.get("PG_HOST", "localhost"),
            port=int(os.environ.get("PG_PORT", 5432)),
            dbname=os.environ.get("PG_DBNAME", "genesis"),
            user=os.environ.get("PG_USER", "genesis"),
            password=os.environ.get("PG_PASSWORD", ""),
        )


def ensure_table(conn) -> None:
    cur = conn.cursor()
    cur.execute(CREATE_REVIEW_REQUESTS_TABLE)
    conn.commit()
    cur.close()


def record_request(conn, request_id: str, contact_id: str, location_id: str,
                   data: Dict[str, Any]) -> None:
    cur = conn.cursor()
    cur.execute(
        """
        INSERT INTO review_requests
            (request_id, contact_id, location_id, first_name, technician,
             job_type, phone, review_link, business_name)
        VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
        ON CONFLICT (request_id) DO NOTHING
        """,
        (
            request_id,
            contact_id,
            location_id,
            data.get("first_name", ""),
            data.get("technician", "the team"),
            data.get("job_type", ""),
            data.get("phone", ""),
            data.get("review_link", ""),
            data.get("business_name", ""),
        )
    )
    conn.commit()
    cur.close()


def mark_sms_sent(conn, request_id: str, followup: bool = False) -> None:
    cur = conn.cursor()
    field = "followup_sent_at" if followup else "sms_sent_at"
    cur.execute(
        f"UPDATE review_requests SET {field} = NOW(), status = %s WHERE request_id = %s",
        ("sms_sent" if not followup else "followup_sent", request_id)
    )
    conn.commit()
    cur.close()


def get_pending_followups(conn, hours_since_initial: int = 48) -> list:
    """Return requests where initial SMS was sent N hours ago with no click."""
    cutoff = datetime.utcnow() - timedelta(hours=hours_since_initial)
    cur = conn.cursor()
    cur.execute(
        """
        SELECT request_id, contact_id, location_id, first_name, technician,
               phone, review_link, business_name
        FROM review_requests
        WHERE status = 'sms_sent'
          AND sms_sent_at <= %s
          AND followup_sent_at IS NULL
          AND clicked_at IS NULL
        """,
        (cutoff,)
    )
    rows = cur.fetchall()
    cur.close()
    return [
        {
            "request_id": r[0], "contact_id": r[1], "location_id": r[2],
            "first_name": r[3], "technician": r[4], "phone": r[5],
            "review_link": r[6], "business_name": r[7],
        }
        for r in rows
    ]


def get_review_stats(conn, location_id: str) -> Dict[str, Any]:
    """Return response rate statistics for a GHL location."""
    cur = conn.cursor()
    cur.execute(
        """
        SELECT
            COUNT(*) as total,
            COUNT(CASE WHEN clicked_at IS NOT NULL THEN 1 END) as clicked,
            COUNT(CASE WHEN followup_sent_at IS NOT NULL THEN 1 END) as followups_sent
        FROM review_requests
        WHERE location_id = %s
        """,
        (location_id,)
    )
    row = cur.fetchone()
    cur.close()
    total = row[0] or 0
    clicked = row[1] or 0
    return {
        "location_id": location_id,
        "total_requests": total,
        "total_clicked": clicked,
        "followups_sent": row[2] or 0,
        "click_rate_pct": round((clicked / total * 100), 1) if total else 0.0,
        "target_pct": 30.0,
    }


# ── GHL SMS sender ─────────────────────────────────────────────────────────────
def send_ghl_sms(contact_id: str, location_id: str, message: str,
                  api_key: str) -> bool:
    """Send SMS via GHL Conversations API."""
    try:
        import requests as req
        url = f"{GHL_API_BASE}/conversations/messages"
        headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
            "Version": GHL_API_VERSION,
        }
        payload = {
            "type": "SMS",
            "contactId": contact_id,
            "message": message,
        }
        resp = req.post(url, headers=headers, json=payload, timeout=15)
        if resp.status_code in (200, 201):
            logger.info(f"SMS sent to contact {contact_id}")
            return True
        else:
            logger.error(f"SMS send failed: {resp.status_code} — {resp.text[:200]}")
            return False
    except Exception as e:
        logger.error(f"SMS send exception: {e}")
        return False


# ── Core review request logic ──────────────────────────────────────────────────
def send_review_request(
    contact_id: str,
    location_id: str,
    first_name: str,
    technician: str,
    job_type: str,
    phone: str,
    review_link: str,
    business_name: str,
    api_key: str,
    dry_run: bool = False,
) -> Dict[str, Any]:
    """
    Send a review request SMS and record it in PostgreSQL.
    Returns result dict with status and request_id.
    """
    # Build request ID for dedup
    raw_id = f"{contact_id}_{location_id}_{datetime.utcnow().strftime('%Y%m%d')}"
    request_id = "rr_" + hashlib.md5(raw_id.encode()).hexdigest()[:12]

    # Format SMS
    message = REVIEW_SMS_TEMPLATE.format(
        first_name=first_name or "there",
        business_name=business_name,
        technician=technician or "our team",
        review_link=review_link,
    )

    if dry_run:
        logger.info(f"[DRY RUN] Would send SMS to {phone}: {message[:80]}...")
        return {
            "request_id": request_id,
            "status": "dry_run",
            "message_preview": message[:100],
        }

    # Send SMS
    sent = send_ghl_sms(contact_id, location_id, message, api_key)

    # Record in PostgreSQL
    conn = None
    try:
        conn = get_pg_connection()
        ensure_table(conn)
        record_request(
            conn, request_id, contact_id, location_id,
            {
                "first_name": first_name,
                "technician": technician,
                "job_type": job_type,
                "phone": phone,
                "review_link": review_link,
                "business_name": business_name,
            }
        )
        if sent:
            mark_sms_sent(conn, request_id)
    except Exception as e:
        logger.error(f"PostgreSQL record failed: {e}")
    finally:
        if conn:
            conn.close()

    return {
        "request_id": request_id,
        "status": "sms_sent" if sent else "sms_failed",
        "contact_id": contact_id,
        "phone": phone,
        "message_length": len(message),
    }


def send_followup_review_requests(api_key: str, dry_run: bool = False) -> Dict[str, Any]:
    """
    Check for pending follow-ups (initial SMS sent 48h ago, no click).
    Sends follow-up SMS for each. Max 1 follow-up per request.
    """
    conn = None
    results = {"processed": 0, "sent": 0, "failed": 0, "dry_run": dry_run}

    try:
        conn = get_pg_connection()
        ensure_table(conn)
        pending = get_pending_followups(conn)
        logger.info(f"Found {len(pending)} pending follow-ups")

        for req in pending:
            results["processed"] += 1
            message = REVIEW_FOLLOWUP_SMS_TEMPLATE.format(
                first_name=req["first_name"] or "there",
                business_name=req["business_name"],
                review_link=req["review_link"],
            )

            if dry_run:
                logger.info(f"[DRY RUN] Follow-up for {req['request_id']}: {message[:60]}...")
                results["sent"] += 1
                continue

            sent = send_ghl_sms(
                req["contact_id"], req["location_id"], message, api_key
            )
            if sent:
                mark_sms_sent(conn, req["request_id"], followup=True)
                results["sent"] += 1
            else:
                results["failed"] += 1

            time.sleep(0.5)  # Polite delay between sends

    except Exception as e:
        logger.error(f"Follow-up run failed: {e}")
    finally:
        if conn:
            conn.close()

    return results


# ── Webhook mode ───────────────────────────────────────────────────────────────
def handle_ghl_webhook(payload: Dict[str, Any], api_key: str,
                        dry_run: bool = False) -> Dict[str, Any]:
    """
    Process a GHL job completion webhook payload.
    Extracts contact fields and dispatches review request.
    """
    contact = payload.get("contact", {})
    custom = payload.get("customData", {})

    contact_id = contact.get("id", "")
    location_id = payload.get("locationId", "")
    first_name = contact.get("firstName", contact.get("first_name", ""))
    phone = contact.get("phone", "")
    technician = custom.get("technician_name", "our team")
    job_type = custom.get("job_type", "")
    review_link = custom.get("google_review_link", "")
    business_name = payload.get("businessName", "")

    if not contact_id or not review_link:
        return {"error": "Missing contact_id or review_link in webhook payload"}

    return send_review_request(
        contact_id=contact_id,
        location_id=location_id,
        first_name=first_name,
        technician=technician,
        job_type=job_type,
        phone=phone,
        review_link=review_link,
        business_name=business_name,
        api_key=api_key,
        dry_run=dry_run,
    )


# ── CLI ───────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="GHL Review Generator Module")
    sub = parser.add_subparsers(dest="command")

    # Send command
    send_p = sub.add_parser("send", help="Send a review request")
    send_p.add_argument("--contact-id", required=True)
    send_p.add_argument("--location-id", required=True)
    send_p.add_argument("--first-name", default="")
    send_p.add_argument("--technician", default="the team")
    send_p.add_argument("--job-type", default="")
    send_p.add_argument("--phone", default="")
    send_p.add_argument("--google-review-link", required=True)
    send_p.add_argument("--business-name", required=True)
    send_p.add_argument("--dry-run", action="store_true")

    # Follow-up command
    fu_p = sub.add_parser("followup", help="Send pending follow-up review requests")
    fu_p.add_argument("--dry-run", action="store_true")

    # Stats command
    stats_p = sub.add_parser("stats", help="Show review request stats")
    stats_p.add_argument("--location-id", required=True)

    # Webhook mode
    wh_p = sub.add_parser("webhook", help="Process GHL webhook from stdin")
    wh_p.add_argument("--dry-run", action="store_true")

    args = parser.parse_args()
    api_key = os.environ.get("GHL_API_KEY", "")

    if args.command == "send":
        result = send_review_request(
            contact_id=args.contact_id,
            location_id=args.location_id,
            first_name=args.first_name,
            technician=args.technician,
            job_type=args.job_type,
            phone=args.phone,
            review_link=args.google_review_link,
            business_name=args.business_name,
            api_key=api_key,
            dry_run=args.dry_run,
        )
        print(json.dumps(result, indent=2))

    elif args.command == "followup":
        result = send_followup_review_requests(api_key=api_key, dry_run=args.dry_run)
        print(json.dumps(result, indent=2))

    elif args.command == "stats":
        conn = get_pg_connection()
        stats = get_review_stats(conn, args.location_id)
        conn.close()
        print(json.dumps(stats, indent=2))

    elif args.command == "webhook":
        payload = json.load(sys.stdin)
        result = handle_ghl_webhook(payload, api_key=api_key, dry_run=args.dry_run)
        print(json.dumps(result, indent=2))

    else:
        parser.print_help()
