#!/usr/bin/env python3
"""
check-subscriptions.py
1. Trae todos los registros de Airtable
2. Filtra por status elegido
3. Match contra /home en VPS1 y VPS2
4. Detecta grace_end vencido → suspende .htaccess
5. Si filtra por Expired → opción de reactivar .htaccess

Modos:
- Interactivo (default, con TTY): pide elegir estado y confirma cada acción.
- Automático (--auto, para cron): usa --state, no pide input, asume "sí" para suspensiones.
- Dry-run (--dry-run): muestra qué haría sin tocar Airtable ni .htaccess.

Cron line:
  0 8 * * * /root/scripts/check-subscriptions.sh --auto --state=Active --no-restore >> /var/log/check-subscriptions.log 2>&1
"""

import os
import sys
import json
import argparse
import subprocess
import urllib.request
import urllib.parse
import urllib.error
from datetime import date

########################################
# ARGS
########################################

parser = argparse.ArgumentParser(description="Check subscriptions (Airtable ↔ /home)")
parser.add_argument("--auto", action="store_true",
                    help="Modo no-interactivo: no pide input, asume 'sí' para suspensiones.")
parser.add_argument("--state", default="Active",
                    help="Status a procesar en modo --auto (default: Active). 'all' procesa todos.")
parser.add_argument("--no-restore", action="store_true",
                    help="No procesar reactivaciones (solo suspensiones).")
parser.add_argument("--dry-run", action="store_true",
                    help="No tocar Airtable ni .htaccess, solo imprimir.")
args = parser.parse_args()

INTERACTIVE = not args.auto and sys.stdin.isatty()

########################################
# CONFIG
########################################

ENV_FILE = "/root/scripts/.airtable.env"
env = {}
if os.path.exists(ENV_FILE):
    with open(ENV_FILE) as f:
        for line in f:
            line = line.strip()
            if "=" in line and not line.startswith("#"):
                k, v = line.split("=", 1)
                env[k.strip()] = v.strip()

AIRTABLE_TOKEN      = env.get("AIRTABLE_TOKEN", "")
AIRTABLE_BASE_ID    = env.get("AIRTABLE_BASE_ID", "")
SUBSCRIPTIONS_TABLE = "tblnpr52JhFBBi2Mg"
AIRTABLE_API_URL    = f"https://api.airtable.com/v0/{AIRTABLE_BASE_ID}/{SUBSCRIPTIONS_TABLE}"

VPS1_IP   = "72.61.45.136"
VPS2_IP   = "179.43.124.219"
VPS2_PORT = "5633"

TODAY = date.today().isoformat()

HTACCESS_SUSPENDED = """# BEWPRO_SUSPENDED
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteRule ^(.*)$ https://bewpro.com/cuenta-suspendida [R=302,L]
</IfModule>
"""

def htaccess_active(username):
    return f"""<IfModule mod_rewrite.c>
  RewriteEngine On

  RewriteRule ^\\.well-known/acme-challenge/ - [L,NC]
  RewriteCond %{{REQUEST_URI}} !^/\\.well-known/acme-challenge/ [NC]

  RewriteCond %{{REQUEST_URI}} !^/git-files/{username}/public/ [NC]
  RewriteRule ^(.*)$ git-files/{username}/public/$1 [L]
</IfModule>
"""

########################################
# AIRTABLE
########################################

def airtable_get_all():
    records = []
    params  = {}
    while True:
        query    = urllib.parse.urlencode(params)
        full_url = f"{AIRTABLE_API_URL}?{query}" if query else AIRTABLE_API_URL
        req = urllib.request.Request(full_url, headers={
            "Authorization": f"Bearer {AIRTABLE_TOKEN}",
            "Content-Type":  "application/json"
        })
        with urllib.request.urlopen(req) as resp:
            data = json.load(resp)
        records.extend(data.get("records", []))
        offset = data.get("offset")
        if not offset:
            break
        params["offset"] = offset
    return records

def airtable_patch(record_id, fields):
    payload = json.dumps({"fields": fields}).encode()
    req = urllib.request.Request(
        f"{AIRTABLE_API_URL}/{record_id}",
        data=payload,
        method="PATCH",
        headers={
            "Authorization": f"Bearer {AIRTABLE_TOKEN}",
            "Content-Type":  "application/json"
        }
    )
    try:
        with urllib.request.urlopen(req) as resp:
            return json.load(resp)
    except urllib.error.HTTPError as e:
        body = e.read().decode()
        raise Exception(f"HTTP {e.code}: {body}")

########################################
# SERVIDORES
########################################

def get_home_users_vps1():
    try:
        r = subprocess.run(["ls", "/home"], capture_output=True, text=True)
        return set(r.stdout.split())
    except Exception as e:
        print(f"  ERROR VPS1 /home: {e}", file=sys.stderr)
        return set()

def get_home_users_vps2():
    try:
        r = subprocess.run(
            ["ssh", "-p", VPS2_PORT, "-o", "StrictHostKeyChecking=no",
             "-o", "ConnectTimeout=5", f"root@{VPS2_IP}", "ls /home"],
            capture_output=True, text=True
        )
        return set(r.stdout.split())
    except Exception as e:
        print(f"  ERROR VPS2 /home: {e}", file=sys.stderr)
        return set()

########################################
# HTACCESS — SUSPEND
########################################

def is_suspended_vps1(username):
    path = f"/home/{username}/public_html/.htaccess"
    try:
        with open(path) as f:
            return "BEWPRO_SUSPENDED" in f.read()
    except:
        return False

def is_suspended_vps2(username):
    path = f"/home/{username}/public_html/.htaccess"
    r = subprocess.run(
        ["ssh", "-p", VPS2_PORT, "-o", "StrictHostKeyChecking=no",
         "-o", "ConnectTimeout=5", f"root@{VPS2_IP}",
         f"grep -q BEWPRO_SUSPENDED {path} 2>/dev/null && echo yes || echo no"],
        capture_output=True, text=True
    )
    return r.stdout.strip() == "yes"

def suspend_vps1(username):
    path = f"/home/{username}/public_html/.htaccess"
    with open(path, "w") as f:
        f.write(HTACCESS_SUSPENDED)
    subprocess.run(["chown", f"{username}:{username}", path])
    print(f"    ✓ .htaccess suspendido en VPS1")

def suspend_vps2(username):
    cmd = f"printf '%s' '{HTACCESS_SUSPENDED}' > /home/{username}/public_html/.htaccess && chown {username}:{username} /home/{username}/public_html/.htaccess"
    r = subprocess.run(
        ["ssh", "-p", VPS2_PORT, "-o", "StrictHostKeyChecking=no",
         "-o", "ConnectTimeout=5", f"root@{VPS2_IP}", cmd],
        capture_output=True, text=True
    )
    if r.returncode == 0:
        print(f"    ✓ .htaccess suspendido en VPS2")
    else:
        print(f"    ✗ ERROR suspendiendo en VPS2: {r.stderr.strip()}")

########################################
# HTACCESS — RESTORE
########################################

def restore_vps1(username):
    path = f"/home/{username}/public_html/.htaccess"
    with open(path, "w") as f:
        f.write(htaccess_active(username))
    subprocess.run(["chown", f"{username}:{username}", path])
    print(f"    ✓ .htaccess restaurado en VPS1")

def restore_vps2(username):
    content = htaccess_active(username)
    cmd = f"printf '%s' '{content}' > /home/{username}/public_html/.htaccess && chown {username}:{username} /home/{username}/public_html/.htaccess"
    r = subprocess.run(
        ["ssh", "-p", VPS2_PORT, "-o", "StrictHostKeyChecking=no",
         "-o", "ConnectTimeout=5", f"root@{VPS2_IP}", cmd],
        capture_output=True, text=True
    )
    if r.returncode == 0:
        print(f"    ✓ .htaccess restaurado en VPS2")
    else:
        print(f"    ✗ ERROR restaurando en VPS2: {r.stderr.strip()}")

########################################
# MAIN
########################################

print("=" * 52)
print("  CHECK SUBSCRIPTIONS —", TODAY)
print("=" * 52)

# 1. Traer registros
print("\nConsultando Airtable...")
all_records = airtable_get_all()
print(f"  Total registros: {len(all_records)}")

# 2. Mostrar estados disponibles
statuses = {}
for r in all_records:
    s = r.get("fields", {}).get("Status", "Sin estado")
    statuses[s] = statuses.get(s, 0) + 1

print("\nEstados disponibles:")
status_list = sorted(statuses.keys())
for i, s in enumerate(status_list, 1):
    print(f"  {i}) {s} ({statuses[s]} registros)")
print(f"  {len(status_list)+1}) Todos")

if INTERACTIVE:
    print()
    choice = input("Seleccioná un estado (número): ").strip()
    try:
        choice_num = int(choice)
        selected_status = None if choice_num == len(status_list)+1 else status_list[choice_num-1]
    except (ValueError, IndexError):
        print("Opción inválida.")
        sys.exit(1)
else:
    # Modo --auto: usa --state
    if args.state.lower() == "all":
        selected_status = None
    elif args.state in status_list:
        selected_status = args.state
    else:
        print(f"\nWARN: --state='{args.state}' no encontrado en estados disponibles. Saliendo sin acción.")
        sys.exit(0)
    print(f"\n[auto] Estado seleccionado: {selected_status or 'Todos'}")

# 3. Filtrar y deduplicar
filtered = [r for r in all_records
            if not selected_status or r.get("fields", {}).get("Status") == selected_status]

seen   = {}
deduped = []
for r in filtered:
    u = r.get("fields", {}).get("Cpanel_User", "").strip()
    if u and u not in seen:
        seen[u] = True
        deduped.append(r)

label = selected_status or "Todos"
print(f"\nFiltrado: {label} → {len(filtered)} registros → {len(deduped)} únicos con Cpanel_User")

# 4. Match contra /home
print("\nObteniendo /home de VPS1...")
vps1_users = get_home_users_vps1()
print(f"  VPS1: {len(vps1_users)} usuarios")

print("Obteniendo /home de VPS2...")
vps2_users = get_home_users_vps2()
print(f"  VPS2: {len(vps2_users)} usuarios")

# 5. Construir tabla
results = []
for r in deduped:
    fields    = r.get("fields", {})
    u         = fields.get("Cpanel_User", "").strip()
    grace_end = fields.get("Grace_Period_End", "").strip()
    status    = fields.get("Status", "")

    if u in vps1_users:
        server = "VPS1"
    elif u in vps2_users:
        server = "VPS2"
    else:
        server = "NOT FOUND"

    action = "ok"
    if grace_end and grace_end <= TODAY and status == "Active":
        action = "suspend"
    elif status == "Expired":
        action = "restore"

    results.append({
        "record_id":      r.get("id"),
        "cpanel_user":    u,
        "app_url":        fields.get("App_URL", ""),
        "grace_end":      grace_end,
        "status":         status,
        "payment_status": fields.get("Payment_Status", ""),
        "server":         server,
        "action":         action,
    })

# 6. Mostrar tabla
print()
print("=" * 75)
print(f"  {'USUARIO':<22} {'STATUS':<12} {'SERVIDOR':<10} {'GRACE END':<12} {'ACCIÓN'}")
print("=" * 75)
for rec in results:
    if rec["action"] == "suspend":
        accion = "⚠  SUSPENDER"
    elif rec["action"] == "restore":
        accion = "↺  REACTIVAR"
    else:
        accion = "✓  ok"
    print(f"  {rec['cpanel_user']:<22} {rec['status']:<12} {rec['server']:<10} {rec['grace_end'] or '-':<12} {accion}")
print("=" * 75)

########################################
# FLUJO SUSPENDER
########################################

to_suspend        = [r for r in results if r["action"] == "suspend" and r["server"] != "NOT FOUND"]
not_found_suspend = [r for r in results if r["action"] == "suspend" and r["server"] == "NOT FOUND"]

if to_suspend:
    print(f"\n  A suspender: {len(to_suspend)} | NOT FOUND (saltados): {len(not_found_suspend)}")

    if not_found_suspend:
        print("\n  WARN — Vencidos pero no encontrados en ningún servidor:")
        for r in not_found_suspend:
            print(f"    - {r['cpanel_user']} (grace: {r['grace_end']})")

    if INTERACTIVE:
        print(f"\n¿Suspender {len(to_suspend)} cuenta(s)? [s/N]: ", end="")
        proceed = input().strip().lower() == "s"
    else:
        print(f"\n[auto] Suspendiendo {len(to_suspend)} cuenta(s)...")
        proceed = True

    if proceed:
        suspended = 0
        errors    = 0
        print()
        for rec in to_suspend:
            u      = rec["cpanel_user"]
            server = rec["server"]
            print(f"  → {u} ({server}, grace: {rec['grace_end']})")
            if args.dry_run:
                print(f"    [dry-run] suspendería .htaccess + Airtable PATCH Status=Expired")
                suspended += 1
                continue
            try:
                if server == "VPS1":
                    if is_suspended_vps1(u):
                        print(f"    ya suspendido, actualizando Airtable...")
                    else:
                        suspend_vps1(u)
                else:
                    if is_suspended_vps2(u):
                        print(f"    ya suspendido, actualizando Airtable...")
                    else:
                        suspend_vps2(u)
                airtable_patch(rec["record_id"], {"Status": "Expired", "Payment_Status": "Past Due"})
                print(f"    ✓ Airtable → Expired / Past Due")
                suspended += 1
            except Exception as e:
                print(f"    ✗ ERROR: {e}")
                errors += 1

        print()
        print("=" * 52)
        print(f"  Suspendidos: {suspended} | Errores: {errors}")
        print("=" * 52)
    else:
        print("Cancelado.")

########################################
# FLUJO REACTIVAR
########################################

to_restore        = [r for r in results if r["action"] == "restore" and r["server"] != "NOT FOUND"]
not_found_restore = [r for r in results if r["action"] == "restore" and r["server"] == "NOT FOUND"]

if to_restore and args.no_restore:
    print(f"\n[auto] Skip reactivación ({len(to_restore)} pendientes) — flag --no-restore activo.")
    to_restore = []

if to_restore:
    print(f"\n  A reactivar: {len(to_restore)} | NOT FOUND (saltados): {len(not_found_restore)}")

    if not_found_restore:
        print("\n  WARN — Expired pero no encontrados en ningún servidor:")
        for r in not_found_restore:
            print(f"    - {r['cpanel_user']}")

    if INTERACTIVE:
        print(f"\n¿Reactivar {len(to_restore)} cuenta(s)? [s/N]: ", end="")
        proceed = input().strip().lower() == "s"
    else:
        # En --auto, NUNCA reactivar automáticamente — requiere criterio humano para fechas.
        print(f"\n[auto] Skip reactivación — requiere modo interactivo.")
        proceed = False

    if proceed:
        from datetime import timedelta
        restored = 0
        errors   = 0

        new_next_payment = (date.today() + timedelta(days=30)).isoformat()
        new_grace_end    = (date.today() + timedelta(days=30)).isoformat()

        print(f"\n  Fechas a aplicar:")
        print(f"    Next_Payment_Date : {new_next_payment}")
        print(f"    Grace_Period_End  : {new_grace_end}")
        print()

        for rec in to_restore:
            u      = rec["cpanel_user"]
            server = rec["server"]
            print(f"  → {u} ({server})")
            if args.dry_run:
                print(f"    [dry-run] restauraría .htaccess + Airtable PATCH Status=Active")
                restored += 1
                continue
            try:
                if server == "VPS1":
                    restore_vps1(u)
                else:
                    restore_vps2(u)
                airtable_patch(rec["record_id"], {
                    "Status":            "Active",
                    "Payment_Status":    "Paid",
                    "Next_Payment_Date": new_next_payment,
                    "Grace_Period_End":  new_grace_end,
                })
                print(f"    ✓ Airtable → Active / Paid / Next: {new_next_payment} / Grace: {new_grace_end}")
                restored += 1
            except Exception as e:
                print(f"    ✗ ERROR: {e}")
                errors += 1

        print()
        print("=" * 52)
        print(f"  Reactivados: {restored} | Errores: {errors}")
        print("=" * 52)
    else:
        print("Cancelado.")

if not to_suspend and not to_restore:
    print("\n  Nada que hacer.")

# Guardar resultados
with open("/tmp/subscription_results.json", "w") as f:
    json.dump(results, f, indent=2)
print(f"\nResultados guardados en /tmp/subscription_results.json")
