"""
push_to_mikrotik.py
====================
Finds all subscribers with payment_status='active' and mikrotik_added=0
and pushes them to the MikroTik hotspot user list, then marks them done.
"""

import os, socket, hashlib
import pymysql
from dotenv import load_dotenv

load_dotenv()

MT_HOST    = os.getenv('MIKROTIK_HOST')
MT_PORT    = int(os.getenv('MIKROTIK_PORT', 8728))
MT_USER    = os.getenv('MIKROTIK_USER')
MT_PASS    = os.getenv('MIKROTIK_PASS')
MT_PROFILE = os.getenv('MIKROTIK_HOTSPOT_PROFILE', 'default')


# ── MikroTik API ─────────────────────────────────────────────
class MikroTikAPI:
    def __init__(self, host, port, username, password):
        self.host = host; self.port = port
        self.username = username; self.password = password
        self.sock = None

    def connect(self):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.settimeout(10)
        self.sock.connect((self.host, self.port))
        print(f"✅ Connected to MikroTik {self.host}:{self.port}")
        self._login()

    def _encode_length(self, n):
        if n < 0x80:       return bytes([n])
        elif n < 0x4000:   n |= 0x8000;    return bytes([(n>>8)&0xFF, n&0xFF])
        elif n < 0x200000: n |= 0xC00000;  return bytes([(n>>16)&0xFF,(n>>8)&0xFF,n&0xFF])
        else:              n |= 0xE0000000; return bytes([(n>>24)&0xFF,(n>>16)&0xFF,(n>>8)&0xFF,n&0xFF])

    def _write_sentence(self, words):
        for word in words:
            enc = word.encode('utf-8')
            self.sock.send(self._encode_length(len(enc)))
            self.sock.send(enc)
        self.sock.send(b'\x00')

    def _read_length(self):
        b = self.sock.recv(1)
        if not b: return 0
        b = b[0]
        if   b & 0x80 == 0: return b
        elif b & 0xC0 == 0x80: return ((b&0x3F)<<8)|self.sock.recv(1)[0]
        elif b & 0xE0 == 0xC0: r=self.sock.recv(2); return ((b&0x1F)<<16)|(r[0]<<8)|r[1]
        elif b & 0xF0 == 0xE0: r=self.sock.recv(3); return ((b&0x0F)<<24)|(r[0]<<16)|(r[1]<<8)|r[2]

    def _read_sentence(self):
        words = []
        while True:
            length = self._read_length()
            if length == 0: break
            words.append(self.sock.recv(length).decode('utf-8', errors='replace'))
        return words

    def _read_response(self):
        responses = []
        while True:
            sentence = self._read_sentence()
            if not sentence: break
            responses.append(sentence)
            if sentence[0] in ('!done', '!trap', '!fatal'): break
        return responses

    def _login(self):
        self._write_sentence(['/login', f'=name={self.username}', f'=password={self.password}'])
        response = self._read_response()
        for sentence in response:
            for word in sentence:
                if word.startswith('=ret='):
                    challenge = word[5:]
                    md5 = hashlib.md5()
                    md5.update(b'\x00')
                    md5.update(self.password.encode('utf-8'))
                    md5.update(bytes.fromhex(challenge))
                    self._write_sentence(['/login', f'=name={self.username}',
                                          f'=response=00{md5.hexdigest()}'])
                    self._read_response()
                    return
        print("✅ Logged in to MikroTik")

    def send(self, words):
        self._write_sentence(words)
        return self._read_response()

    def close(self):
        if self.sock: self.sock.close()


# ── Push a single user ────────────────────────────────────────
def push_user(api, email, password, profile):
    response = api.send(['/ip/hotspot/user/print', f'?name={email}'])

    user_id = None
    for sentence in response:
        for word in sentence:
            if word.startswith('=.id='):
                user_id = word[5:]

    if user_id:
        print(f"   ↳ User exists (id={user_id}), updating...")
        result = api.send([
            '/ip/hotspot/user/set',
            f'=.id={user_id}',
            f'=password={password}',
            f'=profile={profile}',
            '=disabled=no'
        ])
    else:
        print(f"   ↳ New user, adding...")
        result = api.send([
            '/ip/hotspot/user/add',
            f'=name={email}',
            f'=password={password}',
            f'=profile={profile}',
            '=disabled=no'
        ])

    if any('!trap' in str(r) for r in result):
        print(f"   ❌ MikroTik error: {result}")
        return False

    print(f"   ✅ MikroTik: OK")
    return True


# ── Main ──────────────────────────────────────────────────────
def main():
    db = pymysql.connect(
        host=os.getenv('WIFI_DB_HOST', os.getenv('DB_HOST', 'localhost')),
        user=os.getenv('WIFI_DB_USER', os.getenv('DB_USER')),
        password=os.getenv('WIFI_DB_PASS', os.getenv('DB_PASS')),
        database=os.getenv('WIFI_DB_NAME', os.getenv('DB_NAME')),
        cursorclass=pymysql.cursors.DictCursor
    )
    cursor = db.cursor()

    cursor.execute("""
        SELECT id, email, first_name, surname
        FROM subscribers
        WHERE payment_status = 'active'
          AND mikrotik_added = 0
    """)
    pending = cursor.fetchall()

    if not pending:
        print("✅ No pending subscribers — everyone is already on MikroTik.")
        db.close()
        return

    print(f"\n📋 Found {len(pending)} subscriber(s) to push:\n")
    for s in pending:
        print(f"   • {s['first_name']} {s['surname']} <{s['email']}>")

    print()
    api = MikroTikAPI(MT_HOST, MT_PORT, MT_USER, MT_PASS)
    try:
        api.connect()
    except Exception as e:
        print(f"❌ Cannot connect to MikroTik: {e}")
        db.close()
        return

    success_count = 0
    for sub in pending:
        email = sub['email']
        # Hotspot password = email (matches your existing registration pattern)
        print(f"\n🔄 {sub['first_name']} {sub['surname']} ({email})")
        ok = push_user(api, email, email, MT_PROFILE)

        if ok:
            cursor.execute(
                "UPDATE subscribers SET mikrotik_added=1 WHERE id=%s",
                (sub['id'],)
            )
            db.commit()
            print(f"   ✅ DB updated: mikrotik_added=1")
            success_count += 1
        else:
            print(f"   ⚠️  Skipped DB update due to MikroTik error")

    api.close()
    cursor.close()
    db.close()

    print(f"\n{'='*50}")
    print(f"Done. {success_count}/{len(pending)} subscriber(s) pushed successfully.")


if __name__ == '__main__':
    main()
