"""Helpers for choosing the native Feishu delivery format for text replies."""

import ipaddress
import json
import re
import socket
from typing import Callable, Optional, Tuple
from urllib.parse import urljoin, urlparse

import requests


_BLOCK_MARKDOWN = re.compile(
    r"(?m)^\s{0,3}(?:#{1,6}\s|>\s|[-*+]\s|\d+[.)]\s|```|~~~)"
)
_INLINE_MARKDOWN = re.compile(r"(`[^`\n]+`|\*\*[^*\n]+\*\*|\[[^]\n]+\]\([^)\n]+\))")
_TABLE_SEPARATOR = re.compile(r"(?m)^\s*\|?\s*:?-{3,}:?\s*(?:\|\s*:?-{3,}:?\s*)+\|?\s*$")
_MARKDOWN_IMAGE = re.compile(r"!\[([^\]\n]*)\]\(([^)\s]+)\)")
_REDIRECT_CODES = {301, 302, 303, 307, 308}
_MAX_REDIRECTS = 3
_MAX_REMOTE_IMAGE_BYTES = 10 * 1024 * 1024


def contains_markdown(text: str) -> bool:
    """Return whether *text* contains syntax that benefits from card Markdown."""
    if not text:
        return False
    return bool(
        _BLOCK_MARKDOWN.search(text)
        or _INLINE_MARKDOWN.search(text)
        or _TABLE_SEPARATOR.search(text)
    )


def build_markdown_card(text: str) -> dict:
    """Build an inline Card 2.0 payload with one Markdown element."""
    return {
        "schema": "2.0",
        "config": {},
        "body": {
            "elements": [
                {
                    "tag": "markdown",
                    "content": text,
                }
            ]
        },
    }


def build_text_delivery(text: str) -> Tuple[str, str]:
    """Return the Feishu ``msg_type`` and serialized content for a text reply."""
    if contains_markdown(text):
        return "interactive", json.dumps(build_markdown_card(text), ensure_ascii=False)
    return "text", json.dumps({"text": text}, ensure_ascii=False)


def resolve_markdown_images(
    text: str,
    uploader: Callable[[str], Optional[str]],
    max_images: int = 5,
) -> str:
    """Replace remote Markdown image URLs with Feishu image keys."""
    cache = {}
    uploaded = 0

    def replace(match):
        nonlocal uploaded
        alt = match.group(1).strip() or "image"
        target = match.group(2).strip()
        if target.startswith("img_"):
            return match.group(0)
        if urlparse(target).scheme not in ("http", "https"):
            return match.group(0)

        if target not in cache:
            if uploaded >= max_images:
                cache[target] = None
            else:
                uploaded += 1
                try:
                    cache[target] = uploader(target)
                except Exception:
                    cache[target] = None

        image_key = cache[target]
        if image_key:
            return "![{}]({})".format(alt, image_key)
        return "[Image unavailable: {}]".format(alt)

    return _MARKDOWN_IMAGE.sub(replace, text or "")


def validate_public_image_url(url: str) -> None:
    """Reject non-HTTP and non-public image targets before downloading."""
    parsed = urlparse(url)
    if parsed.scheme not in ("http", "https"):
        raise ValueError("unsupported image URL scheme")
    if not parsed.hostname:
        raise ValueError("image URL has no hostname")

    try:
        literal_address = ipaddress.ip_address(parsed.hostname)
        resolved_addresses = [literal_address]
    except ValueError:
        try:
            addresses = socket.getaddrinfo(
                parsed.hostname,
                parsed.port,
                socket.AF_UNSPEC,
                socket.SOCK_STREAM,
            )
        except socket.gaierror as exc:
            raise ValueError("cannot resolve image hostname") from exc
        resolved_addresses = [ipaddress.ip_address(item[4][0]) for item in addresses]

    for address in resolved_addresses:
        if (
            address.is_private
            or address.is_loopback
            or address.is_link_local
            or address.is_reserved
            or address.is_multicast
            or address.is_unspecified
        ):
            raise ValueError("image URL resolves to a non-public address")


def download_public_image(
    url: str,
    get=requests.get,
    max_bytes: int = _MAX_REMOTE_IMAGE_BYTES,
) -> Tuple[bytes, str]:
    """Download a public image with redirect, type, and size checks."""
    current = url
    for _ in range(_MAX_REDIRECTS + 1):
        validate_public_image_url(current)
        response = get(
            current,
            headers={"User-Agent": "CowAgent/Feishu"},
            timeout=(5, 15),
            allow_redirects=False,
            stream=True,
        )

        if response.status_code in _REDIRECT_CODES:
            location = response.headers.get("Location")
            response.close()
            if not location:
                raise ValueError("image redirect has no location")
            current = urljoin(current, location)
            continue

        if response.status_code != 200:
            response.close()
            raise ValueError("image download returned HTTP {}".format(response.status_code))

        content_type = response.headers.get("Content-Type", "").split(";", 1)[0].lower()
        if not content_type.startswith("image/"):
            response.close()
            raise ValueError("remote resource is not an image")

        try:
            content_length = int(response.headers.get("Content-Length") or 0)
        except (TypeError, ValueError):
            content_length = 0
        if content_length > max_bytes:
            response.close()
            raise ValueError("remote image is too large")

        chunks = []
        downloaded = 0
        try:
            for chunk in response.iter_content(chunk_size=8192):
                if not chunk:
                    continue
                downloaded += len(chunk)
                if downloaded > max_bytes:
                    raise ValueError("remote image is too large")
                chunks.append(chunk)
        finally:
            response.close()
        return b"".join(chunks), content_type

    raise ValueError("too many image redirects")


def upload_public_image_to_feishu(
    url: str,
    access_token: str,
    post=requests.post,
) -> Optional[str]:
    """Download a public image and upload its bytes to Feishu."""
    payload, content_type = download_public_image(url)
    extension = {
        "image/jpeg": "jpg",
        "image/png": "png",
        "image/gif": "gif",
        "image/webp": "webp",
        "image/bmp": "bmp",
    }.get(content_type, "img")
    response = post(
        "https://open.feishu.cn/open-apis/im/v1/images",
        headers={"Authorization": "Bearer " + access_token},
        data={"image_type": "message"},
        files={
            "image": (
                "markdown-image.{}".format(extension),
                payload,
                content_type,
            )
        },
        timeout=(5, 15),
    )
    body = response.json()
    if body.get("code") != 0:
        return None
    return (body.get("data") or {}).get("image_key")
