import os
import json
import subprocess
import threading
import telebot
import shutil
import random
import base64
import io
from .config import ConfigManager
from .providers import prepare_image_content

class TerminalConnector(object):
    def listen(self, callback):
        print("\n--- MMClaw Kernel Active (Terminal Mode) ---")
        while True:
            try:
                text = input("👤 You: ").strip()
                if text.lower() in ["exit", "quit"]: break
                if text: callback(text) 
            except KeyboardInterrupt: break

    def send(self, message):
        print(f"\r🐈 MMClaw: {message}\n👤 You: ", end="", flush=True)

    def send_file(self, path):
        full_path = os.path.expanduser(path)
        print(f"\r🐈 MMClaw: [FILE SENT] {os.path.abspath(full_path)}\n👤 You: ", end="", flush=True)

class FeishuConnector(object):
    def __init__(self, app_id, app_secret, config=None):
        try:
            import lark_oapi as lark
        except ImportError:
            raise ImportError("lark-oapi is required for Feishu mode. Install it with: pip install lark-oapi")
            
        self.lark = lark
        self.app_id = app_id
        self.app_secret = app_secret
        self.callback = None
        self.last_message_id = None
        self.config = config if config else ConfigManager.load()
        # Nested connector config
        self.fs_config = self.config.get("connectors", {}).get("feishu", {})
        self.authorized_id = self.fs_config.get("authorized_id")
        
        self.verify_code = str(random.randint(100000, 999999))
        self.client = lark.Client.builder() \
            .app_id(app_id) \
            .app_secret(app_secret) \
            .log_level(lark.LogLevel.INFO) \
            .build()
        self.ws_client = None
        self.stop_on_auth = False
    

    def _handle_message(self, data) -> None:
        from lark_oapi.api.im.v1 import GetMessageResourceRequest
        try:
            sender_id = data.event.sender.sender_id.open_id
            msg_type = data.event.message.message_type
            msg_dict = json.loads(data.event.message.content)
            
            # Always store last message_id for reply attempt
            self.last_message_id = data.event.message.message_id


            if not self.authorized_id:
                text = msg_dict.get("text", "").strip()
                if text == self.verify_code:
                    self.authorized_id = sender_id
                    # Save to nested config
                    if "connectors" not in self.config: self.config["connectors"] = {}
                    if "feishu" not in self.config["connectors"]: self.config["connectors"]["feishu"] = {}
                    self.config["connectors"]["feishu"]["authorized_id"] = sender_id
                    
                    ConfigManager.save(self.config)
                    print(f"\n[⭐] AUTH SUCCESS! MMClaw is now locked to Feishu User: {sender_id}")
                    self.send("🐈 Verification Successful! I am now your personal agent.")
                    
                    if self.stop_on_auth:
                        os._exit(0) # Brutal but effective for a CLI wizard setup
                    return
                else:
                    return

            if sender_id != self.authorized_id:
                return
            
            if msg_type == "text":
                text = msg_dict.get("text", "").strip()
                if text and self.callback:
                    print(f"📩 Feishu: {text}")
                    self.callback(text)
            elif msg_type == "image":
                image_key = msg_dict.get("image_key")
                try:
                    request = GetMessageResourceRequest.builder() \
                        .message_id(self.last_message_id) \
                        .file_key(image_key) \
                        .type("image") \
                        .build()
                    response = self.client.im.v1.message_resource.get(request)
                    if not response.success():
                        print(f"[!] Feishu Image Download Error: {response.code}, {response.msg}")
                        return
                    
                    downloaded_file = response.file.read()
                    content = prepare_image_content(downloaded_file)
                    print(f"📩 Feishu: [Photo] (Compressed)")
                    if self.callback:
                        self.callback(content)
                except Exception as e:
                    print(f"[!] Feishu Photo Error: {e}")
                    self.send(f"Error processing image: {e}")
        except Exception as e:
            print(f"[!] Feishu Parse Error: {e}")
        return None

    def listen(self, callback, stop_on_auth=False):
        self.callback = callback
        self.stop_on_auth = stop_on_auth
        print(f"\n--- MMClaw Kernel Active (Feishu Mode) ---")
        
        if not self.authorized_id:
            print(f"[🔐] 需要进行飞书身份验证")
            print(f"[*] 请将以下 6 位验证码发送给飞书机器人: {self.verify_code}")
        elif stop_on_auth:
            print(f"\n[✅] 飞书身份已验证: {self.authorized_id}")
            return

        event_handler = self.lark.EventDispatcherHandler.builder("", "") \
            .register_p2_im_message_receive_v1(self._handle_message) \
            .build()

        self.ws_client = self.lark.ws.Client(
            app_id=self.app_id,
            app_secret=self.app_secret,
            event_handler=event_handler,
            log_level=self.lark.LogLevel.INFO
        )
        self.ws_client.start()

    def send(self, message):
        from lark_oapi.api.im.v1 import ReplyMessageRequest, ReplyMessageRequestBody
        if not self.last_message_id: 
            return
        
        reply_body = json.dumps({"text": message})
        request = ReplyMessageRequest.builder() \
            .message_id(self.last_message_id) \
            .request_body(ReplyMessageRequestBody.builder() \
                .content(reply_body) \
                .msg_type("text") \
                .build()) \
            .build()
            
        response = self.client.im.v1.message.reply(request)
        if not response.success():
            print(f"[!] Feishu Reply Error: {response.code}, {response.msg}")

    def send_file(self, path):
        from lark_oapi.api.im.v1 import CreateFileRequest, CreateFileRequestBody, ReplyMessageRequest, ReplyMessageRequestBody
        if not self.last_message_id: 
            return
        
        full_path = os.path.expanduser(path)
        if not os.path.exists(full_path):
            self.send(f"❌ File not found: {path}")
            return

        file_name = os.path.basename(full_path)
        
        try:
            # 1. Upload file
            with open(full_path, "rb") as f:
                request = CreateFileRequest.builder() \
                    .request_body(CreateFileRequestBody.builder() \
                        .file_type("stream") \
                        .file_name(file_name) \
                        .file(f) \
                        .build()) \
                    .build()
                response = self.client.im.v1.file.create(request)
                
            if not response.success():
                print(f"[!] Feishu Upload Error: {response.code}, {response.msg}")
                self.send(f"❌ Error uploading file: {response.msg}")
                return
                
            file_key = response.data.file_key
            
            # 2. Send file message (as a reply)
            reply_body = json.dumps({"file_key": file_key})
            request = ReplyMessageRequest.builder() \
                .message_id(self.last_message_id) \
                .request_body(ReplyMessageRequestBody.builder() \
                    .content(reply_body) \
                    .msg_type("file") \
                    .build()) \
                .build()
                
            response = self.client.im.v1.message.reply(request)
            if not response.success():
                print(f"[!] Feishu Send File Error: {response.code}, {response.msg}")
        except Exception as e:
            print(f"[!] Feishu File Process Error: {e}")
            self.send(f"❌ Error processing file: {str(e)}")

class TelegramConnector(object):
    def __init__(self, token, telegram_authorized_user_id):
        self.bot = telebot.TeleBot(token)
        self.telegram_authorized_user_id = int(telegram_authorized_user_id)

    def listen(self, callback):
        print(f"\n--- MMClaw Kernel Active (Telegram Mode) ---")
        print(f"[*] Listening for messages from User ID: {self.telegram_authorized_user_id}")
        
        @self.bot.message_handler(func=lambda message: message.from_user.id == self.telegram_authorized_user_id, 
                                  content_types=['text', 'photo', 'document'])
        def handle_message(message):
            text = message.text or message.caption or ""
            
            if message.content_type == 'photo':
                try:
                    file_id = message.photo[-1].file_id
                    file_info = self.bot.get_file(file_id)
                    downloaded_file = self.bot.download_file(file_info.file_path)
                    
                    content = prepare_image_content(downloaded_file, text if text else "What is in this image?")
                    print(f"📩 Telegram: [Photo] {text} (Compressed)")
                    callback(content)
                except Exception as e:
                    print(f"[!] Telegram Photo Error: {e}")
                    self.send(f"Error processing image: {e}")
            else:
                if text:
                    print(f"📩 Telegram: {text}")
                    callback(text)
            
        @self.bot.message_handler(func=lambda message: message.from_user.id != self.telegram_authorized_user_id,
                                  content_types=['text', 'photo', 'audio', 'video', 'document', 'sticker', 'voice'])
        def unauthorized(message):
            self.bot.reply_to(message, "🚫 Unauthorized. I only respond to my master.")

        self.bot.infinity_polling()

    def send(self, message):
        try:
            self.bot.send_message(self.telegram_authorized_user_id, f"🐈 {message}")
        except Exception as e:
            print(f"[!] Telegram Send Error: {e}")

    def send_file(self, path):
        path = os.path.expanduser(path)
        try:
            with open(path, 'rb') as f:
                self.bot.send_document(self.telegram_authorized_user_id, f)
        except Exception as e:
            self.send(f"Error sending file: {str(e)}")

class WhatsAppConnector(object):
    def __init__(self, config=None):
        self.process = None
        self.callback = None
        self.active_recipient = None
        self.config = config if config else ConfigManager.load()
        # Nested connector config
        self.wa_config = self.config.get("connectors", {}).get("whatsapp", {})
        self.authorized_id = self.wa_config.get("authorized_id")
        
        self.verify_code = str(random.randint(100000, 999999))
        self.last_sent_text = None
        self.bridge_path = os.path.join(os.path.dirname(__file__), "bridge.js")
        self.is_windows = os.name == 'nt'
        self._deps_checked = False

    def _ensure_node(self):
        if not shutil.which("node"):
            print("[❌] Node.js not found. Please install Node.js to use WhatsApp mode.")
            return False
        return True

    def _get_node_env(self):
        """Prepare environment to find global node_modules."""
        env = os.environ.copy()
        try:
            npm_root = subprocess.check_output(["npm", "root", "-g"], encoding='utf-8', stderr=subprocess.DEVNULL, shell=self.is_windows).strip()
            existing_path = env.get("NODE_PATH", "")
            env["NODE_PATH"] = f"{npm_root}{os.pathsep}{existing_path}" if existing_path else npm_root
        except:
            pass
        return env

    def _ensure_deps(self):
        if self._deps_checked:
            return
        
        deps = ["@whiskeysockets/baileys", "qrcode-terminal", "pino"]
        missing = []

        print("[*] Verifying WhatsApp bridge dependencies...")
        env = self._get_node_env()
        
        for dep in deps:
            # Check if we can require the dependency using node
            # This is much faster than 'npm list'
            check = subprocess.run(
                ["node", "-e", f"require('{dep}')"],
                env=env,
                capture_output=True,
                shell=self.is_windows
            )
            
            if check.returncode != 0:
                missing.append(dep)

        if not missing:
            self._deps_checked = True
            return

        print(f"[!] Missing: {', '.join(missing)}")
        print(f"[*] Installing dependencies: {', '.join(missing)}...")

        try:
            # Attempt global install
            print("[*] Running: npm install -g " + " ".join(missing))
            subprocess.run(["npm", "install", "-g"] + missing, check=True, shell=self.is_windows)
        except subprocess.CalledProcessError:
            print("[!] Global install failed. Attempting local install...")
            print("[*] Running: npm install " + " ".join(missing))
            subprocess.run(["npm", "install"] + missing, check=True, shell=self.is_windows)
        
        self._deps_checked = True

    def listen(self, callback, stop_on_auth=False):
        if not self._ensure_node(): return
        self._ensure_deps()
        self.callback = callback
        self.stop_on_auth = stop_on_auth

        if stop_on_auth and self.authorized_id:
            print(f"\n[✅] WhatsApp identity already verified: {self.authorized_id}")
            return
        
        env = self._get_node_env()
        self.process = subprocess.Popen(
            ["node", self.bridge_path],
            stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=None,
            env=env, encoding='utf-8', bufsize=1, shell=self.is_windows
        )

        def output_reader():
            for line in iter(self.process.stdout.readline, ""):
                if line.startswith("JSON_EVENT:"):
                    try:
                        event = json.loads(line[11:])
                        if event["type"] == "message":
                            sender = event["from"]
                            text = event["text"].strip()
                            from_me = event.get("fromMe", False)

                            if not self.authorized_id:
                                if text == self.verify_code:
                                    self.authorized_id = sender
                                    # Save to nested config
                                    if "connectors" not in self.config: self.config["connectors"] = {}
                                    if "whatsapp" not in self.config["connectors"]: self.config["connectors"]["whatsapp"] = {}
                                    self.config["connectors"]["whatsapp"]["authorized_id"] = sender
                                    
                                    ConfigManager.save(self.config)
                                    print(f"\n[⭐] AUTH SUCCESS! MMClaw is now locked to: {sender}")
                                    self.send("🐈 Verification Successful! I am now your personal agent.")
                                    
                                    if stop_on_auth:
                                        self.process.terminate()
                                        return
                                    continue
                                else:
                                    continue

                            if sender != self.authorized_id:
                                continue

                            if from_me and text == self.last_sent_text:
                                continue

                            print(f"📩 WhatsApp: {text}")
                            self.active_recipient = sender
                            if self.callback:
                                self.callback(text)

                        elif event["type"] == "image":
                            sender = event["from"]
                            b64_data = event["base64"]
                            caption = event.get("caption", "").strip()
                            from_me = event.get("fromMe", False)

                            if not self.authorized_id or sender != self.authorized_id:
                                continue

                            # We allow from_me for images because the bot currently only sends 
                            # documents (not imageMessages), so there is no risk of a loop.
                            # This allows users to talk to the bot from the same account.

                            try:
                                # Decode base64 to bytes
                                image_bytes = base64.b64decode(b64_data)
                                content = prepare_image_content(image_bytes, caption if caption else "What is in this image?")
                                print(f"📩 WhatsApp: [Photo] {caption} (Compressed)")
                                self.active_recipient = sender
                                if self.callback:
                                    self.callback(content)
                            except Exception as e:
                                print(f"[!] WhatsApp Image Error: {e}")

                        elif event["type"] == "connected":
                            if not self.authorized_id:
                                print(f"\n[✅] WhatsApp Bridge Connected!")
                                print(f"[🔐] WHATSAPP VERIFICATION REQUIRED")
                                print(f"[*] PLEASE SEND THIS CODE TO YOURSELF ON WHATSAPP: {self.verify_code}")
                            else:
                                print(f"\n[✅] WhatsApp Active")
                                if stop_on_auth:
                                    self.process.terminate()
                                    return

                    except Exception as e:
                        print(f"[!] Bridge Parse Error: {e}")
                else:
                    print(line, end="")

        threading.Thread(target=output_reader, daemon=True).start()
        self.process.wait()

    def send(self, message):
        if not self.process or not (self.active_recipient or self.authorized_id): return
        recipient = self.active_recipient or self.authorized_id
        self.last_sent_text = message
        payload = {"to": recipient, "text": message}
        self.process.stdin.write(f"SEND:{json.dumps(payload)}\n")
        self.process.stdin.flush()

    def send_file(self, path):
        if not self.process or not (self.active_recipient or self.authorized_id): return
        recipient = self.active_recipient or self.authorized_id
        full_path = os.path.abspath(os.path.expanduser(path))
        payload = {"to": recipient, "path": full_path}
        self.process.stdin.write(f"SEND_FILE:{json.dumps(payload)}\n")
        self.process.stdin.flush()
