package cdr

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	ueCallBackTicket "be.ems/features/ue/mf_callback_ticket"
	"be.ems/lib/config"
	"be.ems/lib/core/ctx"
	"be.ems/lib/dborm"
	"be.ems/lib/log"
	"be.ems/lib/services"
	neService "be.ems/src/modules/network_element/service"
	wsService "be.ems/src/modules/ws/service"
)

var (
	UriCDREvent = config.DefaultUriPrefix + "/cdrManagement/v1/elementType/{elementTypeValue}/objectType/cdrEvent"
	UriCDRFile  = config.DefaultUriPrefix + "/cdrManagement/v1/elementType/{elementTypeValue}/objectType/cdrFile"

	CustomUriCDREvent = config.UriPrefix + "/cdrManagement/v1/elementType/{elementTypeValue}/objectType/cdrEvent"
	CustomUriCDRFile  = config.UriPrefix + "/cdrManagement/v1/elementType/{elementTypeValue}/objectType/cdrFile"
)

// CDREvent CDR数据表格结构体
type CDREvent struct {
	NeType    string         `json:"neType" xorm:"ne_type"`
	NeName    string         `json:"neName" xorm:"ne_name"`
	RmUID     string         `json:"rmUID" xorm:"rm_uid"`
	Timestamp int            `json:"timestamp" xorm:"timestamp"`
	CDR       map[string]any `json:"CDR" xorm:"cdr_json"`
}

// PostCDREventFrom 接收CDR数据请求
func PostCDREventFrom(w http.ResponseWriter, r *http.Request) {
	log.Info("PostCDREventFrom processing... ")
	neType := ctx.GetParam(r, "elementTypeValue")
	var cdrEvent CDREvent
	if err := ctx.ShouldBindJSON(r, &cdrEvent); err != nil {
		services.ResponseInternalServerError500ProcessError(w, err)
		return
	}

	neTypeLower := strings.ToLower(cdrEvent.NeType)
	if neType == "" || neType != neTypeLower {
		services.ResponseInternalServerError500ProcessError(w, fmt.Errorf("inconsistent network element types"))
		return
	}

	tableName := fmt.Sprintf("cdr_event_%s", neTypeLower)
	affected, err := dborm.XormInsertTableOne(tableName, cdrEvent)
	if err != nil && affected <= 0 {
		log.Error("Failed to insert "+tableName, err)
		services.ResponseInternalServerError500ProcessError(w, err)
		return
	}

	// 发送到匹配的网元
	neInfo := neService.NewNeInfo.SelectNeInfoByRmuid(cdrEvent.RmUID)
	if neInfo.RmUID == cdrEvent.RmUID {
		// 推送到ws订阅组
		switch neInfo.NeType {
		case "IMS":
			if v, ok := cdrEvent.CDR["recordType"]; ok && (v == "MOC" || v == "MTSM") {
				wsService.NewWSSend.ByGroupID(wsService.GROUP_IMS_CDR+neInfo.NeId, cdrEvent)
			}
		case "SMF":
			wsService.NewWSSend.ByGroupID(wsService.GROUP_SMF_CDR+neInfo.NeId, cdrEvent)
		case "SMSC":
			wsService.NewWSSend.ByGroupID(wsService.GROUP_SMSC_CDR+neInfo.NeId, cdrEvent)
		case "SGWC":
			wsService.NewWSSend.ByGroupID(wsService.GROUP_SGWC_CDR+neInfo.NeId, cdrEvent)
		}
	}

	// MF网元类型特殊处理, 未接电话的回拨工单流转处理
	if neTypeLower == "mf" && cdrEvent.CDR["agentName"] == "" {
		// 发送到MF网元
		// 构造网元MF的API地址
		url := fmt.Sprintf("http://%s:%d/ne/config/data?neType=%s&neId=%s&paramName=agents",
			neInfo.IP, neInfo.Port, neInfo.NeType, neInfo.NeId)
		// 发送HTTP请求获取座席列表
		resp, err := http.Get(url)
		if err != nil {
			log.Error("Failed to get MF agents", err)
			services.ResponseInternalServerError500ProcessError(w, err)
			return
		}
		defer resp.Body.Close()

		// 解析座席列表响应
		var agentResp struct {
			Code int                          `json:"code"`
			Data []ueCallBackTicket.AgentInfo `json:"data"`
			Msg  string                       `json:"msg"`
		}

		if err := json.NewDecoder(resp.Body).Decode(&agentResp); err != nil {
			log.Error("Failed to decode MF agents response", err)
			services.ResponseInternalServerError500ProcessError(w, err)
			return
		}

		// 调用服务获取最新一个被分配工单的座席和下一个要分配的座席
		mfService := ueCallBackTicket.NewCallbackTicketService()
		lastAgent, err := mfService.GetLastAssignedAgent()
		if err != nil {
			log.Error("Failed to get last assigned agent", err)
			// 可以继续执行，不返回错误
		}

		// 选择下一个要分配的座席
		selectedAgent := mfService.SelectNextAgent(agentResp.Data, lastAgent)

		// 创建回调工单
		var updatedAt *int64 = nil
		ticket := ueCallBackTicket.CallbackTicket{
			CallerNumber: cdrEvent.CDR["callerParty"].(string),
			CalleeNumber: cdrEvent.CDR["calledParty"].(string),
			Status:       ueCallBackTicket.TicketStatusNew.Enum(),
			AgentName:    selectedAgent,
			Comment:      "",
			MsdData:      cdrEvent.CDR["msdData"].(string),
			RmUid:        cdrEvent.RmUID,
			CreatedAt:    time.Now().UnixMicro(),
			UpdatedAt:    updatedAt,
		}
		if err := mfService.InsertCallbackTicket(ticket); err != nil {
			log.Error("Failed to insert MF callback ticket", err)
			// services.ResponseInternalServerError500ProcessError(w, err)
			// return
		}
	}

	// MF网元类型特殊处理, 处理座席回拨的工单流转
	if neTypeLower == "mf" && cdrEvent.CDR["recordType"] == "MTC" {
		// 获取座席号码(主叫)和被叫号码
		agentNumber, ok1 := cdrEvent.CDR["callerParty"].(string)
		callerNumber, ok2 := cdrEvent.CDR["calledParty"].(string)

		if !ok1 || !ok2 {
			log.Error("Invalid CDR format: missing callerParty or calledParty")
			services.ResponseInternalServerError500ProcessError(w, fmt.Errorf("invalid CDR format"))
			return
		}

		// 获取通话时长
		callDuration, ok := cdrEvent.CDR["callDuration"].(float64)
		if !ok {
			// 尝试其他可能的类型
			if durationInt, ok := cdrEvent.CDR["callDuration"].(int); ok {
				callDuration = float64(durationInt)
			} else {
				log.Error("Invalid CDR format: callDuration is not a number")
				services.ResponseInternalServerError500ProcessError(w, fmt.Errorf("invalid CDR format"))
				return
			}
		}

		// 通过座席号码和主叫号码查找符合条件的工单
		mfService := ueCallBackTicket.NewCallbackTicketService()
		ticket, err := mfService.FindCallbackTicketByAgentAndCaller(agentNumber, callerNumber)
		if err != nil {
			log.Error("Failed to find callback ticket", err)
			services.ResponseInternalServerError500ProcessError(w, err)
			return
		}

		if ticket == nil {
			// 没有找到对应的工单，可能是手动呼叫，不处理
			log.Warn(fmt.Sprintf("No callback ticket found for agent %s and caller %s", agentNumber, callerNumber))
			services.ResponseStatusOK204NoContent(w)
			return
		}

		// 获取通话信息
		answerTime, _ := cdrEvent.CDR["answerTime"].(string)
		releaseTime, _ := cdrEvent.CDR["releaseTime"].(string)
		cause, _ := cdrEvent.CDR["cause"].(string)

		// 处理回拨结果并更新工单
		if err := mfService.ProcessCallbackResult(ticket, callDuration, answerTime, releaseTime, cause); err != nil {
			log.Error("Failed to process callback result", err)
			services.ResponseInternalServerError500ProcessError(w, err)
			return
		}

		log.Info(fmt.Sprintf("Successfully processed callback for ticket %d", ticket.TicketId))
	}

	services.ResponseStatusOK204NoContent(w)
}
