from flask import Blueprint, request, jsonify
import random, time
import requests
import xml.etree.ElementTree as ET
from datetime import datetime

from config import VOIPMS_ENDPOINT, ENDPOINT_OBSCURITY

bp = Blueprint('yeastar', __name__)

@bp.route(f"/{ENDPOINT_OBSCURITY}/yeastar-outbound", methods=['POST'])
@bp.route(f"/{ENDPOINT_OBSCURITY}/yeastar-outbound/verbose", methods=['POST'])
def handle_yeastar_outbound():
    verbose = request.path.endswith('/verbose')

    ip = request.headers.get('X-Forwarded-For', request.remote_addr)

    try:
        data = request.get_json(force=True)
    except Exception:
        return jsonify({
            "errors": [{
                "code": "10002",
                "title": "Invalid JSON",
                "detail": "The request body must be valid JSON."
            }]
        }), 400

    from_number = data.get('from')
    to_number = data.get('to')
    text = data.get('text', '')
    media_urls = data.get('media_urls', [])

    is_mms = len(media_urls) > 0 or len(text) > 160

    if not media_urls and len(text) > 160:
        is_mms = True

    auth_header = request.headers.get('Authorization', '')
    username, password = parse_bearer_token(auth_header)
    if not username or not password:
        return jsonify({
            "errors": [{
                "code": "10004",
                "title": "Invalid Authorization",
                "detail": "Bearer token is missing or invalid."
            }]
        }), 400

    envelope = build_soap_envelope(username, password, from_number, to_number, text, media_urls if is_mms else [])

    if verbose:
        print(f"===== Yeastar Verbose Request =====")
        print(f"IP: {ip}")
        print(f"Type: {'MMS' if is_mms else 'SMS'}")
        print(f"From: {from_number} ? To: {to_number}")
        print(f"Text: {text}")
        print(f"Media: {media_urls}")
        print(f"Envelope:\n{envelope}")
        print("==============================")

    response = requests.post(
        VOIPMS_ENDPOINT,
        data=envelope.encode('utf-8'),
        headers={'Content-Type': 'text/xml'}
    )

    if verbose:
        print(f"SOAP Raw Response: {response.text}")

    # Robust SOAP status parse
    status = None
    try:
        root = ET.fromstring(response.text)
        for item in root.iter():
            if item.tag.endswith('item'):
                key = item.find('./key')
                if key is not None and key.text == 'status':
                    value = item.find('./value')
                    if value is not None:
                        status = value.text
                        break
    except Exception as e:
        if verbose:
            print(f"Failed to parse SOAP response: {e}")

    # === Determine pass/fail ===
    result = "PASS" if status == "success" else "FAIL"

    if verbose:
        print(f"Parsed SOAP Status: {status}")
        print(f"Result: {result}")

    msg_id = generate_message_id()

    # Basic always-on log
    print(f"[{datetime.now().isoformat()}] [{result}] IP:{ip} From Yeastar | {'MMS' if is_mms else 'SMS'} | From:{from_number} ? To:{to_number} | Images:{len(media_urls)}")

    # === Error code mapping ===
    error_code_map = {
        "invalid_number": "10001",
        "invalid_param": "10002",
        "unsupported_media": "10003",
        "auth_fail": "10004",
        "no_permission": "10005",
        "too_many_requests": "10006",
        "service_unavailable": "10007",
        "media_toolong": "10008",
    }

    if status == "success":
        return jsonify({"data": {"id": msg_id}}), 200
    else:
        code = error_code_map.get(status, "10002")
        return jsonify({
            "errors": [{
                "code": code,
                "title": "Message Failed",
                "detail": f"VOIP.ms returned status: {status or 'unknown'}"
            }]
        }), 400

def generate_message_id():
    return f"{random.randint(100000, 999999)}{int(time.time())}"

def parse_bearer_token(auth_header):
    """
    Expect: Authorization: Bearer username|||password
    """
    if not auth_header.startswith("Bearer "):
        return None, None
    token = auth_header.split("Bearer ")[1].strip()
    parts = token.split("|||")
    if len(parts) == 2:
        return parts[0], parts[1]
    return None, None

def build_soap_envelope(
    username, password, from_number, to_number,
    message_text, media_urls=None
):
    media_urls = media_urls or []
    is_mms = len(media_urls) > 0
    soap_method = "sendMMS" if is_mms else "sendSMS"

    envelope = f"""
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:wsd="https://voip.ms/api/wsdl"
                  xmlns:xsd1="https://voip.ms/api/schema">
   <soapenv:Header/>
   <soapenv:Body>
      <wsd:{soap_method}>
         <wsd:params>
            <xsd1:api_username>{username}</xsd1:api_username>
            <xsd1:api_password>{password}</xsd1:api_password>
            <xsd1:did>{from_number}</xsd1:did>
            <xsd1:dst>{to_number}</xsd1:dst>
            <xsd1:message>{message_text}</xsd1:message>
"""

    # Add media tags if MMS, max 3 allowed
    if is_mms:
        for i, url in enumerate(media_urls[:3]):
            envelope += f"            <xsd1:media{i+1}>{url}</xsd1:media{i+1}>\n"

    envelope += f"""         </wsd:params>
      </wsd:{soap_method}>
   </soapenv:Body>
</soapenv:Envelope>"""

    return envelope.strip()