from typing import Dict

import httpx

from app.config import settings
from app.posting_policy import can_post_private_note
from app.schemas import ActionResult
import os


def build_private_note(action_result: ActionResult) -> str:
    return f"""🤖 ClientFlow

Ação:
{action_result.action}

Resumo:
{action_result.note}
"""


async def add_private_note(
    conversation_id: str,
    action_result: ActionResult,
) -> Dict:
    if os.getenv("CLIENTFLOW_DISABLE_CHATWOOT_PRIVATE_NOTES", "true").lower() in {"1", "true", "yes", "sim"}:
        return {
            "enabled": False,
            "status": "disabled_by_env",
            "reason": "CLIENTFLOW_DISABLE_CHATWOOT_PRIVATE_NOTES=true",
        }

    if not settings.chatwoot_write_enabled:
        return {
            "enabled": False,
            "status": "skipped",
            "reason": "CHATWOOT_WRITE_ENABLED=false",
            "action_result": action_result.model_dump(),
        }

    if not can_post_private_note(action_result):
        return {
            "enabled": True,
            "status": "skipped",
            "reason": "posting_policy_blocked",
            "action_result": action_result.model_dump(),
        }

    if not settings.chatwoot_base_url or not settings.chatwoot_account_id or not settings.chatwoot_api_token:
        return {
            "enabled": True,
            "status": "skipped",
            "reason": "missing Chatwoot config",
            "action_result": action_result.model_dump(),
        }

    url = (
        settings.chatwoot_base_url.rstrip("/")
        + f"/api/v1/accounts/{settings.chatwoot_account_id}"
        + f"/conversations/{conversation_id}/messages"
    )

    payload = {
        "content": build_private_note(action_result),
        "message_type": "outgoing",
        "private": True,
        "content_type": "text",
        "content_attributes": {},
    }

    headers = {
        "Content-Type": "application/json",
        "api_access_token": settings.chatwoot_api_token,
    }

    async with httpx.AsyncClient(timeout=30) as client:
        response = await client.post(url, headers=headers, json=payload)

    if response.status_code >= 400:
        return {
            "enabled": True,
            "status": "failed",
            "status_code": response.status_code,
            "body": response.text,
            "action_result": action_result.model_dump(),
        }

    return {
        "enabled": True,
        "status": "sent",
        "status_code": response.status_code,
        "response": response.json(),
        "action_result": action_result.model_dump(),
    }



def _chatwoot_message_url(conversation_id: str) -> str:
    return (
        settings.chatwoot_base_url.rstrip("/")
        + f"/api/v1/accounts/{settings.chatwoot_account_id}"
        + f"/conversations/{conversation_id}/messages"
    )


def _chatwoot_ready() -> Dict:
    if not settings.chatwoot_write_enabled:
        return {"enabled": False, "status": "skipped", "reason": "CHATWOOT_WRITE_ENABLED=false"}
    if not settings.chatwoot_base_url or not settings.chatwoot_account_id or not settings.chatwoot_api_token:
        return {"enabled": True, "status": "skipped", "reason": "missing Chatwoot config"}
    return {"enabled": True, "status": "ready"}



def format_chatwoot_public_message(content: str) -> str:
    """Prepare operator-written text for Chatwoot public messages.

    Chatwoot renders public message content as Markdown-like text. In that
    renderer, blank-line paragraph breaks are preserved but single newlines
    inside a paragraph can be collapsed into spaces. That breaks operator
    replies with price lists written as separate lines using "–" bullets.

    The textarea remains plain text in ClientFlow. Before sending, we add
    Markdown hard-break markers (two trailing spaces before line breaks) to
    non-empty lines, so the text keeps the same visual line breaks in
    Chatwoot without requiring operators to know Markdown.
    """
    text = str(content or "").replace("\r\n", "\n").replace("\r", "\n").strip()
    if not text:
        return ""

    formatted_lines: list[str] = []
    for line in text.split("\n"):
        clean_line = line.rstrip()
        if not clean_line:
            formatted_lines.append("")
            continue
        formatted_lines.append(clean_line + "  ")

    return "\n".join(formatted_lines).strip()


async def send_public_message(conversation_id: str, content: str) -> Dict:
    """Send a public outgoing Chatwoot message.

    This is used by the ClientFlow reply assistant.  It is deliberately gated by
    CHATWOOT_WRITE_ENABLED and returns a structured result instead of raising for
    normal configuration skips.
    """
    conversation_id = str(conversation_id or "").strip()
    content = format_chatwoot_public_message(content)
    if not conversation_id:
        return {"enabled": settings.chatwoot_write_enabled, "status": "failed", "reason": "missing conversation_id"}
    if not content:
        return {"enabled": settings.chatwoot_write_enabled, "status": "failed", "reason": "empty content"}

    ready = _chatwoot_ready()
    if ready.get("status") != "ready":
        return ready

    payload = {
        "content": content,
        "message_type": "outgoing",
        "private": False,
        "content_type": "text",
        "content_attributes": {},
    }
    headers = {
        "Content-Type": "application/json",
        "api_access_token": settings.chatwoot_api_token,
    }

    async with httpx.AsyncClient(timeout=30) as client:
        response = await client.post(_chatwoot_message_url(conversation_id), headers=headers, json=payload)

    if response.status_code >= 400:
        return {"enabled": True, "status": "failed", "status_code": response.status_code, "body": response.text}
    return {"enabled": True, "status": "sent", "status_code": response.status_code, "response": response.json()}


async def send_public_message_with_attachments(
    conversation_id: str,
    content: str,
    *,
    attachments: list[dict] | None = None,
) -> Dict:
    """Send a public message and optional PDF attachments through Chatwoot.

    ``attachments`` items must contain filename, content bytes and content_type.
    Chatwoot expects multipart/form-data with ``attachments[]`` fields when
    files are present.
    """
    attachments = attachments or []
    if not attachments:
        return await send_public_message(conversation_id, content)

    conversation_id = str(conversation_id or "").strip()
    content = format_chatwoot_public_message(content)
    if not conversation_id:
        return {"enabled": settings.chatwoot_write_enabled, "status": "failed", "reason": "missing conversation_id"}
    if not content:
        return {"enabled": settings.chatwoot_write_enabled, "status": "failed", "reason": "empty content"}

    ready = _chatwoot_ready()
    if ready.get("status") != "ready":
        return ready

    data = {
        "content": content,
        "message_type": "outgoing",
        "private": "false",
        "content_type": "text",
    }
    files = []
    for attachment in attachments:
        filename = str(attachment.get("filename") or "documento.pdf")
        file_bytes = attachment.get("content") or b""
        content_type = str(attachment.get("content_type") or "application/pdf")
        files.append(("attachments[]", (filename, file_bytes, content_type)))

    headers = {"api_access_token": settings.chatwoot_api_token}
    async with httpx.AsyncClient(timeout=60) as client:
        response = await client.post(_chatwoot_message_url(conversation_id), headers=headers, data=data, files=files)

    if response.status_code >= 400:
        return {"enabled": True, "status": "failed", "status_code": response.status_code, "body": response.text}
    return {"enabled": True, "status": "sent", "status_code": response.status_code, "response": response.json()}
