#!/bin/bash
# Deploy all fixes:
# 1. Fix _is_automated_email (was rejecting ALL Gmail users)
# 2. Sender fingerprinting (distinguish sender vs recipient opens)
# 3. DB migration for sender_fingerprint column
#
# Run from the signalmark directory on the server:
#   bash deploy_all_fixes.sh

set -e

echo "=== Deploying all fixes ==="

# Backup
cp gmail_client.py gmail_client.py.bak.$(date +%Y%m%d%H%M)
cp app.py app.py.bak.$(date +%Y%m%d%H%M)

# Fix 1: gmail_client.py — _is_automated_email
python3 -c "
with open('gmail_client.py', 'r') as f:
    content = f.read()

if '_extract_sender_domain' in content:
    print('  gmail_client.py: already fixed')
else:
    content = content.replace(
        '_AUTOMATED_SENDERS = {\n    \"identity.olitt.net\",\n    \"accounts.google.com\",\n    \"google.com\",\n    \"gmail.com\",\n    \"googlemail.com\",\n    \"no-reply.google.com\",\n    \"workspace.google.com\",\n}',
        '_AUTOMATED_SENDERS = {\n    \"identity.olitt.net\",\n    \"accounts.google.com\",\n    \"no-reply.google.com\",\n    \"workspace.google.com\",\n    \"mail.google.com\",\n}'
    )
    content = content.replace(
        'def _is_automated_email(headers: dict) -> bool:',
        '''def _extract_sender_domain(from_raw: str) -> str:
    \"\"\"Extract the domain from a From header value.\"\"\"
    import re as _re
    angle = _re.search(r\"<[^>]*@([^>]+)>\", from_raw)
    if angle:
        return angle.group(1).lower().strip()
    bare = _re.search(r\"@([\\\\w.-]+\\\\.[a-zA-Z]{2,})\", from_raw)
    if bare:
        return bare.group(1).lower().strip()
    return \"\"


def _is_automated_email(headers: dict) -> bool:'''
    )
    content = content.replace(
        '    # Check sender domain\n    for domain in _AUTOMATED_SENDERS:\n        if domain in from_raw:\n            return True',
        '    # Check sender domain\n    sender_domain = _extract_sender_domain(from_raw)\n    if sender_domain in _AUTOMATED_SENDERS:\n        return True'
    )
    with open('gmail_client.py', 'w') as f:
        f.write(content)
    print('  gmail_client.py: fixed')
"

# Fix 2: app.py — sender fingerprint + domain fix
python3 << 'PYEOF'
with open('app.py', 'r') as f:
    content = f.read()

changes = 0

# Add sender_fingerprint to EXTRA_COLUMNS
if '"sender_fingerprint"' not in content:
    content = content.replace(
        '("click_count", "INTEGER NOT NULL DEFAULT 0"),\n)',
        '("click_count", "INTEGER NOT NULL DEFAULT 0"),\n    ("sender_fingerprint", "TEXT"),\n)'
    )
    print('  app.py: added sender_fingerprint column')
    changes += 1

# Add _normalize_ua and _is_sender_viewing before _mark_track_opened
if '_normalize_ua' not in content:
    old_fn = 'def _mark_track_opened(track_id: str) -> bool:'
    new_fn = '''def _normalize_ua(ua: str) -> str:
    import re as _re
    normalized = _re.sub(r'(Chrome|Firefox|Safari|Edg|OPR|MSIE|Trident)/[\\d.]+', r'\\1/', ua)
    normalized = _re.sub(r'Windows NT [\\d.]+', 'Windows NT', normalized)
    normalized = _re.sub(r'Version/[\\d.]+', 'Version/', normalized)
    return normalized.strip()


def _is_sender_viewing(sender_fingerprint: str) -> bool:
    if not sender_fingerprint:
        return False
    current_ua = request.headers.get("User-Agent", "")
    if not current_ua:
        return False
    return _normalize_ua(current_ua) == _normalize_ua(sender_fingerprint)


def _mark_track_opened(track_id: str) -> bool:'''
    content = content.replace(old_fn, new_fn)
    print('  app.py: added fingerprint comparison functions')
    changes += 1

# Replace the old time-based _mark_track_opened body with fingerprint-based
old_body = '''def _mark_track_opened(track_id: str) -> bool:
    """Mark a track as opened if it exists and is still 'sent'.

    Ignores pixel loads within 10 minutes of track creation — this
    filters out the sender opening their own email and Gmail prefetch.
    Only loads AFTER that window count as the recipient opening it.
    Returns True if the record was actually updated (first open)."""
    db = get_db()
    row = db.execute("SELECT * FROM tracks WHERE track_id = ?", (track_id,)).fetchone()
    if not row:
        return False
    # Time guard: ignore loads within 10 minutes of creation
    # This catches: sender opening own email, Gmail prefetch, email clients
    # that pre-fetch images.  Real recipient opens happen later.
    SELF_OPEN_GUARD_SECONDS = 600  # 10 minutes
    if row["created_at"]:
        try:
            created = datetime.fromisoformat(row["created_at"])
            if created.tzinfo is None:
                created = created.replace(tzinfo=timezone.utc)
            age = (datetime.now(timezone.utc) - created).total_seconds()
            if age < SELF_OPEN_GUARD_SECONDS:
                return False  # too early — likely sender or prefetch
        except Exception:
            pass
    if row["status"] == "opened":
        return False  # already recorded'''

new_body = '''def _mark_track_opened(track_id: str) -> bool:
    """Mark a track as opened if it exists and is still 'sent'.

    Uses sender fingerprinting: the sender's User-Agent is recorded
    when the track is created.  If the pixel request matches that
    fingerprint, it's the sender opening their own email — ignored.
    Only pixel loads from a DIFFERENT fingerprint count as the
    recipient opening the email.
    Returns True if the record was actually updated (first real open)."""
    db = get_db()
    row = db.execute("SELECT * FROM tracks WHERE track_id = ?", (track_id,)).fetchone()
    if not row:
        return False
    if row["status"] == "opened":
        return False  # already recorded
    # Sender fingerprint check
    sender_fp = row["sender_fingerprint"] if "sender_fingerprint" in row.keys() else None
    if sender_fp and _is_sender_viewing(sender_fp):
        return False
    # Fallback: short time guard for tracks without fingerprint (sync-created)
    if not sender_fp and row["created_at"]:
        try:
            created = datetime.fromisoformat(row["created_at"])
            if created.tzinfo is None:
                created = created.replace(tzinfo=timezone.utc)
            age = (datetime.now(timezone.utc) - created).total_seconds()
            if age < 30:
                return False  # Gmail prefetch
        except Exception:
            pass'''

if old_body in content:
    content = content.replace(old_body, new_body)
    print('  app.py: _mark_track_opened now uses fingerprint')
    changes += 1

# Fix sender domain check
old_domain = '    # Skip automated/system emails (OTP, notifications, Google internal)\n    from_header = (meta.get("from") or "").lower()\n    for domain in gmail_client._AUTOMATED_SENDERS:\n        if domain in from_header:\n            return False'
new_domain = '    # Skip automated/system emails (OTP, notifications, Google internal)\n    from_header = (meta.get("from") or "").lower()\n    sender_domain = gmail_client._extract_sender_domain(from_header)\n    if sender_domain in gmail_client._AUTOMATED_SENDERS:\n        return False'
if old_domain in content:
    content = content.replace(old_domain, new_domain)
    print('  app.py: sender domain check fixed')
    changes += 1

with open('app.py', 'w') as f:
    f.write(content)

if changes == 0:
    print('  app.py: all fixes already applied')
PYEOF

# DB migration: add sender_fingerprint column
python3 -c "
import sqlite3
db = sqlite3.connect('signalmark.db')
cols = [row[1] for row in db.execute('PRAGMA table_info(tracks)')]
if 'sender_fingerprint' not in cols:
    db.execute('ALTER TABLE tracks ADD COLUMN sender_fingerprint TEXT')
    db.commit()
    print('  DB: added sender_fingerprint column')
else:
    print('  DB: sender_fingerprint column exists')
db.close()
"

echo ""
echo "=== Restarting app ==="
pkill -f 'app.py' || true
sleep 2
echo ""
echo "Done! Start with:"
echo "  cd ~/signalmark && PORT=5055 HOST=0.0.0.0 python3 app.py"
echo ""
echo "=== Test sync (should now find sent emails): ==="
echo "  curl -s -X POST http://127.0.0.1:5055/api/gmail/sync | python3 -m json.tool"
