import logging
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

logger = logging.getLogger(__name__)

# ── Persistent connection pool (reuses TCP connections for speed) ──────────
# This eliminates the overhead of a fresh TLS handshake per message.
# connect=3s: fail fast on unreachable host
# read=8s:    enough for Meta API to respond; shorter than the old 10s
_session = requests.Session()
_adapter = HTTPAdapter(
    pool_connections=4,
    pool_maxsize=16,
    max_retries=Retry(
        total=3,
        backoff_factor=0.5,          # 0.5s → 1s → 2s
        status_forcelist=[429, 500, 502, 503, 504],
        allowed_methods=["POST"],
        raise_on_status=False,
    ),
)
_session.mount("https://", _adapter)
_session.mount("http://", _adapter)

_TIMEOUT = (3, 8)   # (connect_timeout, read_timeout)
_ACTION_TIMEOUT = (2, 4)  # Faster timeout for typing/seen actions (non-critical)


# ── Typing Indicator & Mark Seen ───────────────────────────────────────────

def send_fb_sender_action(token, psid, action):
    """
    Sends a sender_action to Facebook Messenger.
    action: 'typing_on' | 'typing_off' | 'mark_seen'
    Returns True on success, False on failure (never raises).
    """
    url = f"https://graph.facebook.com/v17.0/me/messages?access_token={token}"
    payload = {
        "recipient": {"id": psid},
        "sender_action": action,
    }
    try:
        response = _session.post(url, json=payload, timeout=_ACTION_TIMEOUT)
        if response.status_code == 200:
            logger.debug("FB sender_action '%s' sent to PSID %s", action, psid)
            return True
        else:
            logger.warning(
                "FB sender_action '%s' failed for PSID %s: %s",
                action, psid, response.text[:200]
            )
            return False
    except Exception as e:
        logger.warning("FB sender_action '%s' error for PSID %s: %s", action, psid, e)
        return False


def send_fb_typing_on(company, psid):
    """Show the '...' typing bubble in Messenger — call before AI generation."""
    token = company.fb_page_access_token
    if not token:
        return False
    return send_fb_sender_action(token, psid, "typing_on")


def send_fb_typing_off(company, psid):
    """Hide the typing bubble — call after sending the reply."""
    token = company.fb_page_access_token
    if not token:
        return False
    return send_fb_sender_action(token, psid, "typing_off")


def send_fb_mark_seen(company, psid):
    """
    Mark the incoming message as seen (shows the eye/seen icon on sender's side).
    Call this immediately when the webhook is received.
    """
    token = company.fb_page_access_token
    if not token:
        return False
    return send_fb_sender_action(token, psid, "mark_seen")


# ── Main Outgoing Message Dispatcher ──────────────────────────────────────

def send_social_reply_direct(company, visitor_id, text, attachment_url=None, message_type='text'):
    """
    Directly sends a message to Meta API without requiring a ChatMessage DB object.
    This eliminates DB reads/updates, significantly speeding up reply delivery.
    Returns True if sent successfully, False otherwise.
    """
    if not text and not attachment_url:
        return False

    success = False

    if visitor_id.startswith("fb_"):
        if not getattr(company, 'fb_messenger_enabled', False):
            logger.info("FB Messenger disabled for %s; skipping send", company.name)
            return False
        psid = visitor_id[3:]
        # Turn off typing indicator before sending
        send_fb_typing_off(company, psid)
        success = send_fb_messenger_reply(company, psid, text, attachment_url, message_type)

    elif visitor_id.startswith("wa_"):
        if not getattr(company, 'whatsapp_enabled', False):
            logger.info("WhatsApp disabled for %s; skipping send", company.name)
            return False
        phone = visitor_id[3:]
        success = send_wa_reply(company, phone, text, attachment_url, message_type)

    return success


def send_outgoing_social_message(msg_id):
    """
    Dispatches a bot/agent ChatMessage to Meta's Graph API (FB or WA).
    Uses a persistent session with automatic retries on transient errors.
    Returns True if sent successfully, False otherwise.
    """
    from chatapp.models import ChatMessage
    try:
        msg = ChatMessage.objects.get(id=msg_id)
    except ChatMessage.DoesNotExist:
        logger.error("ChatMessage id=%s not found for outgoing social send", msg_id)
        return False

    visitor_id = msg.visitor_id or ""
    company = msg.company
    text = msg.content or ""
    attachment_url = msg.attachment.url if msg.attachment else msg.attachment_url
    file_path = msg.attachment.path if msg.attachment else None
    message_type = msg.message_type

    if not text and not attachment_url:
        return False

    msg.delivery_attempts += 1
    success = False

    if visitor_id.startswith("fb_"):
        if not company.fb_messenger_enabled:
            logger.info("FB Messenger disabled for %s; skipping send", company.name)
            msg.delivery_status = 'failed'
            msg.save(update_fields=['delivery_status', 'delivery_attempts'])
            return False
        psid = visitor_id[3:]
        # Turn off typing indicator before sending (it clears automatically, but explicit is cleaner)
        send_fb_typing_off(company, psid)
        success = send_fb_messenger_reply(company, psid, text, attachment_url, message_type, file_path)

    elif visitor_id.startswith("wa_"):
        if not company.whatsapp_enabled:
            logger.info("WhatsApp disabled for %s; skipping send", company.name)
            msg.delivery_status = 'failed'
            msg.save(update_fields=['delivery_status', 'delivery_attempts'])
            return False
        phone = visitor_id[3:]
        success = send_wa_reply(company, phone, text, attachment_url, message_type)

    msg.delivery_status = 'sent' if success else 'failed'
    msg.save(update_fields=['delivery_status', 'delivery_attempts'])
    return success


def send_fb_messenger_reply(company, psid, text, attachment_url=None, message_type='text', file_path=None):
    token = company.fb_page_access_token
    if not token:
        logger.warning("FB Page Access Token not configured for %s", company.name)
        return False

    url = f"https://graph.facebook.com/v17.0/me/messages?access_token={token}"

    # Skip sending text chunks if the text is just the auto-generated placeholder
    placeholder = f"[{message_type.upper()}]"
    if text and text.strip() == placeholder:
        chunks = []
    else:
        # Facebook limits message text to 2000 chars
        chunks = [text[i:i + 1950] for i in range(0, len(text), 1950)] if text else []

    all_success = True
    
    if attachment_url:
        fb_type = 'audio' if message_type == 'voice' else message_type
        if fb_type not in ['image', 'audio', 'video', 'file']:
            fb_type = 'file'
            
        if file_path:
            import mimetypes
            import json
            content_type, _ = mimetypes.guess_type(file_path)
            if not content_type:
                content_type = 'application/octet-stream'
            
            try:
                with open(file_path, 'rb') as f:
                    filename = file_path.split('/')[-1] if '/' in file_path else file_path.split('\\')[-1]
                    files = {
                        'filedata': (filename, f, content_type)
                    }
                    payload = {
                        'recipient': json.dumps({"id": psid}),
                        'message': json.dumps({"attachment": {"type": fb_type, "payload": {"is_reusable": True}}})
                    }
                    response = _session.post(url, data=payload, files=files, timeout=_TIMEOUT)
                    res_data = response.json()
                    if response.status_code == 200:
                        logger.info("FB Messenger sent file attachment OK to PSID %s", psid)
                    else:
                        all_success = False
                        err_msg = res_data.get('error', {}).get('message', 'Unknown FB API error')
                        err_type = res_data.get('error', {}).get('type', 'API_ERROR')
                        logger.error("FB Messenger file attachment send failed [%s]: %s", err_type, err_msg)
                        _log_integration_error(company, 'facebook', err_type, err_msg, str(res_data))
            except requests.exceptions.Timeout:
                all_success = False
                logger.error("FB Messenger API timed out for PSID %s", psid)
                _log_integration_error(company, 'facebook', 'TIMEOUT', f"Read timed out sending file attachment to PSID {psid}")
            except Exception as e:
                all_success = False
                logger.exception("FB Messenger API error: %s", e)
                _log_integration_error(company, 'facebook', 'CONNECTION_ERROR', str(e))
        else:
            # Fallback to URL method
            # Ensure attachment_url is absolute
            final_url = attachment_url
            if hasattr(attachment_url, 'startswith') and attachment_url.startswith('/'):
                domain = company.allowed_domain if company and company.allowed_domain else 'localhost:8000'
                # Strip any trailing slash from domain
                domain = domain.rstrip('/')
                final_url = f"https://{domain}{attachment_url}"

            payload = {
                "recipient": {"id": psid},
                "message": {
                    "attachment": {
                        "type": fb_type,
                        "payload": {
                            "url": final_url,
                            "is_reusable": True
                        }
                    }
                }
            }
            
            try:
                headers = {"Content-Type": "application/json"}
                response = _session.post(url, headers=headers, json=payload, timeout=_TIMEOUT)
                res_data = response.json()
                if response.status_code == 200:
                    logger.info("FB Messenger sent attachment OK to PSID %s", psid)
                else:
                    all_success = False
                    err_msg = res_data.get('error', {}).get('message', 'Unknown FB API error')
                    err_type = res_data.get('error', {}).get('type', 'API_ERROR')
                    logger.error("FB Messenger attachment send failed [%s]: %s", err_type, err_msg)
                    _log_integration_error(company, 'facebook', err_type, err_msg, str(res_data))
            except requests.exceptions.Timeout:
                all_success = False
                logger.error("FB Messenger API timed out for PSID %s", psid)
                _log_integration_error(company, 'facebook', 'TIMEOUT', f"Read timed out sending attachment to PSID {psid}")
            except Exception as e:
                all_success = False
                logger.exception("FB Messenger API error: %s", e)
                _log_integration_error(company, 'facebook', 'CONNECTION_ERROR', str(e))
    
    for chunk in chunks:
        payload = {
            "recipient": {"id": psid},
            "message": {"text": chunk},
        }
        try:
            headers = {"Content-Type": "application/json"}
            response = _session.post(url, headers=headers, json=payload, timeout=_TIMEOUT)
            res_data = response.json()
            if response.status_code == 200:
                logger.info("FB Messenger sent OK to PSID %s", psid)
            else:
                all_success = False
                err_msg = res_data.get('error', {}).get('message', 'Unknown FB API error')
                err_type = res_data.get('error', {}).get('type', 'API_ERROR')
                logger.error("FB Messenger send failed [%s]: %s", err_type, err_msg)
                _log_integration_error(company, 'facebook', err_type, err_msg, str(res_data))
        except requests.exceptions.Timeout:
            all_success = False
            logger.error("FB Messenger API timed out for PSID %s", psid)
            _log_integration_error(company, 'facebook', 'TIMEOUT', f"Read timed out sending to PSID {psid}")
        except Exception as e:
            all_success = False
            logger.exception("FB Messenger API error: %s", e)
            _log_integration_error(company, 'facebook', 'CONNECTION_ERROR', str(e))

    return all_success


def send_wa_reply(company, phone, text, attachment_url=None, message_type='text'):
    token = company.wa_access_token
    phone_number_id = company.wa_phone_number_id
    if not token or not phone_number_id:
        logger.warning("WhatsApp credentials missing for %s", company.name)
        return False

    url = f"https://graph.facebook.com/v17.0/{phone_number_id}/messages"
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json",
    }

    chunks = [text[i:i + 4000] for i in range(0, len(text), 4000)] if text else []

    all_success = True
    
    if attachment_url:
        wa_type = 'audio' if message_type == 'voice' else message_type
        wa_type = 'document' if wa_type == 'file' else wa_type
        if wa_type not in ['image', 'audio', 'video', 'document']:
            wa_type = 'document'
            
        payload = {
            "messaging_product": "whatsapp",
            "to": phone,
            "type": wa_type,
            wa_type: {
                "link": attachment_url
            }
        }
        try:
            response = _session.post(url, headers=headers, json=payload, timeout=_TIMEOUT)
            res_data = response.json()
            if response.status_code in [200, 201]:
                logger.info("WhatsApp sent attachment OK to %s", phone)
            else:
                all_success = False
                err_msg = res_data.get('error', {}).get('message', 'Unknown WA API error')
                err_type = res_data.get('error', {}).get('type', 'API_ERROR')
                logger.error("WhatsApp attachment send failed [%s]: %s", err_type, err_msg)
                _log_integration_error(company, 'whatsapp', err_type, err_msg, str(res_data))
        except requests.exceptions.Timeout:
            all_success = False
            logger.error("WhatsApp API timed out for phone %s", phone)
            _log_integration_error(company, 'whatsapp', 'TIMEOUT', f"Read timed out sending attachment to {phone}")
        except Exception as e:
            all_success = False
            logger.exception("WhatsApp API error: %s", e)
            _log_integration_error(company, 'whatsapp', 'CONNECTION_ERROR', str(e))
    for chunk in chunks:
        payload = {
            "messaging_product": "whatsapp",
            "to": phone,
            "type": "text",
            "text": {"body": chunk},
        }
        try:
            response = _session.post(url, headers=headers, json=payload, timeout=_TIMEOUT)
            res_data = response.json()
            if response.status_code in [200, 201]:
                logger.info("WhatsApp sent OK to %s", phone)
            else:
                all_success = False
                err_msg = res_data.get('error', {}).get('message', 'Unknown WA API error')
                err_type = res_data.get('error', {}).get('type', 'API_ERROR')
                logger.error("WhatsApp send failed [%s]: %s", err_type, err_msg)
                _log_integration_error(company, 'whatsapp', err_type, err_msg, str(res_data))
        except requests.exceptions.Timeout:
            all_success = False
            logger.error("WhatsApp API timed out for phone %s", phone)
            _log_integration_error(company, 'whatsapp', 'TIMEOUT', f"Read timed out sending to {phone}")
        except Exception as e:
            all_success = False
            logger.exception("WhatsApp API error: %s", e)
            _log_integration_error(company, 'whatsapp', 'CONNECTION_ERROR', str(e))

    return all_success


def _log_integration_error(company, platform, error_type, error_message, details=None):
    """Write a row to IntegrationErrorLog without crashing the caller."""
    try:
        from chatapp.models import IntegrationErrorLog
        IntegrationErrorLog.objects.create(
            company=company,
            platform=platform,
            error_type=error_type,
            error_message=error_message,
            details=details or "",
        )
    except Exception as ex:
        logger.error("Failed to write IntegrationErrorLog: %s", ex)
