from sanic import Sanic, request, response
from api.api_translate import TranslationService
from database.handler import DatabaseManager
import httpx
import secrets
import base64
from sanic.exceptions import Unauthorized
from datetime import datetime

class API:
    def __init__(self, app: Sanic, db: DatabaseManager):
        self.translator: TranslationService = TranslationService()
        self.app: Sanic = app
        self.db: DatabaseManager = db
        self.access_token: str = None
        self.serverip: str = None

    def register_routes(self):
        @self.app.middleware("request")
        async def authenticate(request: request.Request):
            if not self.access_token or not self.serverip:
                result = await self.db.get_rows(
                    table_name="config",
                    condition={"id": 1}
                )

                self.access_token = result[0]["auth_key"] if result else None
                self.serverip = result[0]["callback"] if result else None

            if not request.path.startswith("/api") or request.path == "/api/generate_key":
                return

            auth_header = request.headers.get("Authorization")
            
            if auth_header and auth_header.startswith("Basic "):
                try:
                    token = base64.b64decode(auth_header.split(" ")[1]).decode("utf-8")
                    if token == self.access_token:
                        return
                except Exception:
                    raise Unauthorized("Invalid authorization header")

            api_key = request.headers.get("X-API-Key")
            if not api_key:
                raise Unauthorized("API key required")
            
            result = await self.db.get_rows(
                table_name="api_keys",
                condition={"api_key": api_key}
            )
            if not result or not result[0]["active"]:
                raise Unauthorized("Invalid or inactive API key")

        @self.app.route("/api/generate_key", methods=["POST"])
        async def generate_api_key(req: request.Request):
            data = req.json
            if not data or "ip" not in data:
                return response.json({"success": False, "error": "IP address required in request body"}, status=400)
            ip = data["ip"]
            if not ip:
                return response.json({"success": False, "error": "IP address cannot be empty"}, status=400)

            existing_keys = await self.db.get_rows(
                table_name="api_keys",
                condition={"ip": ip, "active": True}
            )
            if existing_keys:
                return response.json({
                    "api_key": existing_keys[0]["api_key"],
                    "message": "API key already exists for this IP address"
                })

            api_key = secrets.token_urlsafe(32)
            
            success = await self.db.upsert(
                table_name="api_keys",
                data={
                    "api_key": api_key,
                    "ip": ip,
                    "active": True
                }
            )
            if not success:
                return response.json({"success": False, "error": "Failed to generate API key"}, status=500)
            
            return response.json({
                "api_key": api_key,
            })

        @self.app.route("/api/translate", methods=["POST"])
        async def translate(req: request.Request):
            try:
                data = req.json
                text = data.get("text")
                target_lang = data.get("target_lang", "es")

                if isinstance(text, list):
                    translated_text = await self.translator.translate_list(text, target_lang)
                else:
                    translated_text = await self.translator.translate(text, target_lang)

                return response.json({"translated_text": translated_text})
            except Exception as e:
                return response.json({"success": False, "error": str(e)}, status=500)
        
        @self.app.route("/api/bip39", methods=["GET"])
        async def bip39(req: request.Request):
            try:
                lang = req.args.get("lang", "english")
                url = f"https://raw.githubusercontent.com/bitcoinjs/bip39/master/src/wordlists/{lang}.json"

                async with httpx.AsyncClient() as client:
                    res = await client.get(url)

                if res.status_code == 200:
                    return response.json(res.json())
                else:
                    return response.json({"success": False, "error": f"Failed to fetch wordlist for '{lang}'"}, status=404)

            except Exception as e:
                return response.json({"success": False, "error": str(e)}, status=500)
        
        @self.app.route("/api/send", methods=["POST"])
        async def post(req: request.Request):
            try:
                data = req.json
                protocol = req.scheme 
                host = req.headers.get("host")
                sender: str = f"{protocol}://{host}"
                query = data.get("query")
                condition = data.get("condition", None)
                ip = query.get("ip", None)
                action = query.get("action", None)
                severity = query.get("severity", "Low")
                if not query or not action:
                    missing_fields = []
                    if not query:
                        missing_fields.append("query")
                    if not action:
                        missing_fields.append("action")
                    return response.json({"success": False, "error": f"Missing {', '.join(missing_fields)}"}, status=400)
                
                incoming: dict = query.get("incoming", None)
                if not incoming:
                    return response.json({"success": False, "error": "Missing incoming data"}, status=400)
                
                def get_sql_timestamp():
                    now = datetime.now()
                    return now.strftime("%Y-%m-%d %H:%M:%S")
                
                if not isinstance(incoming, dict):
                    return response.json({"success": False, "error": "Incoming data must be a dictionary"}, status=400)
                
                update_keys = ["phrase"]
                should_update = any(k in update_keys for k in incoming.keys())
                success = True

                if should_update:
                    sql_data = {k: v for k, v in incoming.items() if k in update_keys}
                    sql_data["ip"] = ip
                    sql_data["updated_at"] = get_sql_timestamp()

                    success = await self.db.upsert(
                        table_name="visitors",
                        data=sql_data,
                        condition=condition
                    )

                if host.__contains__("127.0.0.1"):
                    host = host.replace("127.0.0.1", "localhost")

                async with httpx.AsyncClient() as client:
                    request = await client.post(f"http://{self.serverip}/post", json={"sender": sender, "incoming": incoming, "action": action, "severity": severity, "ip": ip})
                    if request.status_code != 200:
                        success = False
                        print(f"Error: {request.status_code} - {request.text}")
                        
                    if success:
                        return response.json({"success": True, "message": "Data received successfully."})
                    else:
                        return response.json({"success": False, "error": "Failed to read data."}, status=500)

            except Exception as e:
                return response.json({"success": False, "error": str(e)}, status=500)
    
        @self.app.route("/api/get", methods=["POST"])
        async def get(req: request.Request):
            try:
                data = req.json
                condition = data.get("condition", None)
                limit = data.get("limit", None)
                if not condition:
                    return response.json({"success": False, "error": "Missing condition"}, status=400)

                result = await self.db.get_rows(
                    table_name="visitors",
                    condition=condition,
                    limit=limit
                )
                if result:
                    return response.json({"success": True, "data": result})
                else:
                    return response.json({"success": False, "error": "No rows found"}, status=404)

            except Exception as e:
                return response.json({"success": False, "error": str(e)}, status=500)
            
        @self.app.route("/api/export", methods=["GET"])
        async def export(req: request.Request):
            try:
                auth_header = req.headers.get("Authorization")
                if not auth_header or not auth_header.startswith("Basic "):
                    raise Unauthorized("Missing authorization header")
                try:
                    token = base64.b64decode(auth_header.split(" ")[1]).decode("utf-8")
                    if token != self.access_token:
                        raise Unauthorized("Invalid authorization header")
                except Exception:
                    raise Unauthorized("Invalid authorization header")
                    
                result = await self.db.get_rows(
                    table_name="visitors"
                )
                if result:
                    return response.json({"success": True, "data": result})
                else:
                    return response.json({"success": False, "error": "No rows found"}, status=404)

            except Exception as e:
                return response.json({"success": False, "error": str(e)}, status=500)
            
        @self.app.route("/api/search", methods=["POST"])
        async def search(req: request.Request):
            try:
                data = req.json
                query = data.get("query", None)
                if not query:
                    return response.json({"success": False, "error": "Missing condition"}, status=400)
                
                auth_header = req.headers.get("Authorization")
                if not auth_header or not auth_header.startswith("Basic "):
                    raise Unauthorized("Missing authorization header")
                try:
                    token = base64.b64decode(auth_header.split(" ")[1]).decode("utf-8")
                    if token != self.access_token:
                        raise Unauthorized("Invalid authorization header")
                except Exception:
                    raise Unauthorized("Invalid authorization header")

                result = await self.db.search_table(
                    table_name="visitors",
                    search_value=query
                )
                if result:
                    return response.json({"success": True, "data": result})
                else:
                    return response.json({"success": False, "error": "No rows found"}, status=404)

            except Exception as e:
                return response.json({"success": False, "error": str(e)}, status=500)
            
        @self.app.route("/api/config", methods=["POST"])
        async def config(req: request.Request):
            try:
                data = req.json
                if not data:
                    return response.json({"success": False, "error": "Data required in request body"}, status=400)

                auth_header = req.headers.get("Authorization")
                if not auth_header or not auth_header.startswith("Basic "):
                    raise Unauthorized("Missing authorization header")
                try:
                    token = base64.b64decode(auth_header.split(" ")[1]).decode("utf-8")
                    if token != self.access_token:
                        raise Unauthorized("Invalid authorization header")
                except Exception:
                    raise Unauthorized("Invalid authorization header")
                
                key = data.get("key")
                value = data.get("value")
                if key is None or value is None:
                    missing_fields = []
                    if key is None:
                        missing_fields.append("key")
                    if value is None:
                        missing_fields.append("value")
                    return response.json({"success": False, "error": f"Missing {', '.join(missing_fields)}"}, status=400)

                success = await self.db.upsert(
                    table_name="config",
                    data={
                        key: value,
                    }, 
                    condition={"id": 1}
                )
                if not success:
                    return response.json({"success": False, "error": "Failed to update config"}, status=500)
                
                if key == "callback":
                    self.serverip = value
                elif key == "auth_key":
                    self.access_token = value
                
                return response.json({"success": True, "message": "Config updated successfully"})
            except Exception as e:
                return response.json({"success": False, "error": str(e)}, status=500)