package handler

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"regexp"

	"git.yeij.top/AskaEth/Cyrene/platform-bridge/internal/bridge"
)

// Regex patterns for markdown stripping.
var (
	mdBoldRe          = regexp.MustCompile(`\*\*(.+?)\*\*`)
	mdItalicRe        = regexp.MustCompile(`\*(.+?)\*`)
	mdStrikethroughRe = regexp.MustCompile(`~~(.+?)~~`)
	mdHeadingRe       = regexp.MustCompile(`(?m)^#{1,6}\s+`)
)

// BridgeHandler exposes the Platform Bridge REST API.
type BridgeHandler struct {
	router         *bridge.PlatformRouter
	internalToken  string
	channelsMu     sync.RWMutex
	cachedChannels []bridge.ChannelInfo
	logFn          func(platform, channelID, senderID, content string, success bool) // outgoing message logger
}

func NewBridgeHandler(router *bridge.PlatformRouter) *BridgeHandler {
	return &BridgeHandler{
		router:        router,
		internalToken: os.Getenv("INTERNAL_SERVICE_TOKEN"),
	}
}

// SetLogFunc sets the outgoing message logger callback.
func (h *BridgeHandler) SetLogFunc(fn func(platform, channelID, senderID, content string, success bool)) {
	h.logFn = fn
}

func (h *BridgeHandler) RegisterRoutes(mux *http.ServeMux) {
	mux.HandleFunc("/health", h.health)
	mux.HandleFunc("/api/v1/platforms", h.listPlatforms)
	mux.HandleFunc("/api/v1/platforms/", h.platformInfo)
	mux.HandleFunc("/api/v1/identities", h.listIdentities)
	mux.HandleFunc("/api/v1/internal/send-proactive", h.sendProactive)
	mux.HandleFunc("/api/v1/channels", h.listChannels)
	mux.HandleFunc("/api/v1/webhook/telegram", h.telegramWebhook)
	mux.HandleFunc("/api/v1/webhook/", h.genericWebhook)
}

func (h *BridgeHandler) health(w http.ResponseWriter, r *http.Request) {
	writeJSON(w, http.StatusOK, map[string]interface{}{
		"status": "ok", "service": "platform-bridge",
		"platforms": h.router.ListAdapters(),
	})
}

func (h *BridgeHandler) listPlatforms(w http.ResponseWriter, r *http.Request) {
	names := h.router.ListAdapters()
	type platformSummary struct {
		Name      string                      `json:"name"`
		Connected bool                        `json:"connected"`
		Caps      bridge.PlatformCapabilities `json:"capabilities"`
	}
	var platforms []platformSummary
	for _, name := range names {
		a, err := h.router.GetAdapter(name)
		if err != nil {
			continue
		}
		platforms = append(platforms, platformSummary{
			Name:      name,
			Connected: a.IsConnected(),
			Caps:      a.Capabilities(),
		})
	}
	writeJSON(w, http.StatusOK, map[string]interface{}{"platforms": platforms, "total": len(platforms)})
}

func (h *BridgeHandler) platformInfo(w http.ResponseWriter, r *http.Request) {
	name := r.URL.Path[len("/api/v1/platforms/"):]
	a, err := h.router.GetAdapter(name)
	if err != nil {
		writeJSON(w, http.StatusNotFound, errResp(err.Error()))
		return
	}
	writeJSON(w, http.StatusOK, map[string]interface{}{
		"name":         name,
		"connected":    a.IsConnected(),
		"capabilities": a.Capabilities(),
	})
}

func (h *BridgeHandler) listIdentities(w http.ResponseWriter, r *http.Request) {
	all := h.router.ListAllIdentities()
	writeJSON(w, http.StatusOK, all)
}

// telegramWebhook receives updates from Telegram Bot API.
func (h *BridgeHandler) telegramWebhook(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		writeJSON(w, http.StatusMethodNotAllowed, errResp("method not allowed"))
		return
	}

	// Parse into a generic map first to check for message presence.
	var raw map[string]interface{}
	if err := json.NewDecoder(r.Body).Decode(&raw); err != nil {
		writeJSON(w, http.StatusBadRequest, errResp("invalid telegram update"))
		return
	}
	if _, hasMsg := raw["message"]; !hasMsg {
		writeJSON(w, http.StatusOK, map[string]string{"status": "ignored"})
		return
	}

	_, err := h.router.RouteMessage("telegram", raw)
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, errResp(err.Error()))
		return
	}
	writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
}

// genericWebhook receives standard webhook payloads.
func (h *BridgeHandler) genericWebhook(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		writeJSON(w, http.StatusMethodNotAllowed, errResp("method not allowed"))
		return
	}

	// Extract platform name from path: /api/v1/webhook/{platform}
	platform := r.URL.Path[len("/api/v1/webhook/"):]
	if platform == "" || platform == "telegram" {
		writeJSON(w, http.StatusBadRequest, errResp("specify platform name in path"))
		return
	}

	var payload map[string]interface{}
	if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
		writeJSON(w, http.StatusBadRequest, errResp("invalid JSON payload"))
		return
	}

	response, err := h.router.RouteMessage(platform, &payload)
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, errResp(err.Error()))
		return
	}

	// Convert to platform-specific format.
	msgs, err := h.router.SendResponse(response)
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, errResp(err.Error()))
		return
	}

	writeJSON(w, http.StatusOK, map[string]interface{}{
		"messages": msgs,
		"reply_to": response.ReplyTo,
	})
}

// sendProactive handles internal proactive message delivery to platform adapters.
// POST /api/v1/internal/send-proactive
func (h *BridgeHandler) sendProactive(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		writeJSON(w, http.StatusMethodNotAllowed, errResp("method not allowed"))
		return
	}

	// Validate internal token
	token := r.Header.Get("X-Internal-Token")
	if h.internalToken == "" || token != h.internalToken {
		writeJSON(w, http.StatusUnauthorized, errResp("unauthorized"))
		return
	}

	var req struct {
		Platform string `json:"platform"`
		ChatType string `json:"chat_type"`
		UserID   string `json:"user_id"`
		GroupID  string `json:"group_id"`
		AtUserID string `json:"at_user_id"`
		Content  string `json:"content"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeJSON(w, http.StatusBadRequest, errResp("invalid request body"))
		return
	}

	if req.Platform == "" || req.ChatType == "" || req.Content == "" {
		writeJSON(w, http.StatusBadRequest, errResp("platform, chat_type, and content are required"))
		return
	}

	// Map chat type to OBv11 message_type
	msgType := req.ChatType
	if msgType != "private" && msgType != "group" {
		writeJSON(w, http.StatusBadRequest, errResp("chat_type must be private or group"))
		return
	}

	userID := parseIntSafe(req.UserID)
	groupID := parseIntSafe(req.GroupID)

	// 过滤 <action>/<app> 标签，去除 markdown 标记
	content := filterActions(req.Content)
	content = convertMarkdownPlain(content)

	// 按 \n\n 和 ♪ 拆分为多条消息
	messages := splitProactiveContent(content)

	// Prepend CQ @mention tag if at_user_id is specified
	atPrefix := ""
	if req.AtUserID != "" {
		atPrefix = fmt.Sprintf("[CQ:at,qq=%s] ", req.AtUserID)
	}

	// Resolve adapter
	adapterName := req.Platform
	var sendErr error
	for i, msg := range messages {
		fullMsg := atPrefix + msg
		if i > 0 {
			atPrefix = "" // only first message gets @mention
		}
		sendErr = h.router.SendProactive(adapterName, msgType, userID, groupID, fullMsg)
		if sendErr != nil {
			// Fallback: try other adapters with same platform name
			for _, name := range h.router.ListAdapters() {
				if a, aErr := h.router.GetAdapter(name); aErr == nil && a.PlatformName() == req.Platform && a.IsConnected() {
					adapterName = name
					sendErr = h.router.SendProactive(name, msgType, userID, groupID, fullMsg)
					break
				}
			}
		}
		if sendErr != nil {
			log.Printf("[send-proactive] 发送失败: adapter=%s err=%v", adapterName, sendErr)
			break
		}
		log.Printf("[send-proactive] 已发送: adapter=%s chat=%s user=%d group=%d at=%s len=%d msg=%d/%d",
			adapterName, msgType, userID, groupID, req.AtUserID, len(fullMsg), i+1, len(messages))
		if h.logFn != nil {
			chID := req.GroupID
			if msgType == "private" {
				chID = req.UserID
			}
			h.logFn(req.Platform, chID, "Cyrene", fullMsg, true)
		}
	}
	if sendErr != nil {
		writeJSON(w, http.StatusInternalServerError, errResp("send failed: "+sendErr.Error()))
		return
	}

	writeJSON(w, http.StatusOK, map[string]interface{}{
		"success": true,
		"message": "消息已发送",
	})
}

func parseIntSafe(s string) int64 {
	if s == "" {
		return 0
	}
	n, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		return 0
	}
	return n
}

func errResp(msg string) map[string]string {
	return map[string]string{"error": msg}
}

func writeJSON(w http.ResponseWriter, status int, data interface{}) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	json.NewEncoder(w).Encode(data)
}

// filterActions removes <action> and <app> tags and their content.
func filterActions(text string) string {
	tags := [][2]string{
		{"<action>", "</action>"},
		{"<app>", "</app>"},
	}
	for _, t := range tags {
		openTag, closeTag := t[0], t[1]
		for {
			start := strings.Index(text, openTag)
			if start == -1 {
				break
			}
			end := strings.Index(text[start:], closeTag)
			if end == -1 {
				text = text[:start] + text[start+len(openTag):]
				continue
			}
			text = text[:start] + text[start+end+len(closeTag):]
		}
	}
	return strings.TrimSpace(text)
}

// convertMarkdownPlain strips basic markdown formatting.
func convertMarkdownPlain(md string) string {
	md = mdBoldRe.ReplaceAllString(md, "$1")
	md = mdItalicRe.ReplaceAllString(md, "$1")
	md = mdStrikethroughRe.ReplaceAllString(md, "$1")
	md = mdHeadingRe.ReplaceAllString(md, "")
	return md
}

// splitProactiveContent splits long proactive content into multiple messages.
// Strategy same as splitContent in cmd/main.go: split by \n\n, then by ♪.
func splitProactiveContent(text string) []string {
	rawParts := strings.Split(text, "\n\n")
	var parts []string
	for _, p := range rawParts {
		p = strings.TrimSpace(p)
		if p == "" {
			continue
		}
		if strings.Contains(p, "♪") {
			for _, sub := range strings.Split(p, "♪") {
				sub = strings.TrimSpace(sub)
				if sub != "" {
					parts = append(parts, sub)
				}
			}
		} else {
			parts = append(parts, p)
		}
	}

	// Merge very short segments with neighbors (min 8 runes).
	const minRunes = 8
	var merged []string
	for _, part := range parts {
		if len([]rune(part)) < minRunes && len(merged) > 0 {
			merged[len(merged)-1] = merged[len(merged)-1] + part
		} else {
			merged = append(merged, part)
		}
	}
	return merged
}
