package service

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"log/slog"
	"net"
	"net/http"
	"strconv"

	"github.com/emiago/sipgo/sip"
	"github.com/ossrs/srs-sip/pkg/models"
	"github.com/ossrs/srs-sip/pkg/service/stack"
	"golang.org/x/net/html/charset"
)

const GB28181_ID_LENGTH = 20

func (s *UAS) isSameIP(addr1, addr2 string) bool {
	ip1, _, err1 := net.SplitHostPort(addr1)
	ip2, _, err2 := net.SplitHostPort(addr2)

	// 如果解析出错，回退到完整字符串比较
	if err1 != nil || err2 != nil {
		return addr1 == addr2
	}

	return ip1 == ip2
}

func (s *UAS) onRegister(req *sip.Request, tx sip.ServerTransaction) {
	id := req.From().Address.User
	if len(id) != GB28181_ID_LENGTH {
		slog.Error("invalid device ID")
		return
	}

	slog.Debug(fmt.Sprintf("Received REGISTER %s", req.String()))

	if s.conf.GB28181.Auth.Enable {
		// Check if Authorization header exists
		authHeader := req.GetHeaders("Authorization")

		// If no Authorization header, send 401 response to request authentication
		if len(authHeader) == 0 {
			nonce := GenerateNonce()
			resp := stack.NewUnauthorizedResponse(req, http.StatusUnauthorized, "Unauthorized", nonce, s.conf.GB28181.Realm)
			_ = tx.Respond(resp)
			return
		}

		// Validate Authorization
		authInfo := ParseAuthorization(authHeader[0].Value())
		if !ValidateAuth(authInfo, s.conf.GB28181.Auth.Password) {
			slog.Error("auth failed", "device_id", id, "source", req.Source())
			s.respondRegister(req, http.StatusForbidden, "Auth Failed", tx)
			return
		}
	}

	isUnregister := false
	if exps := req.GetHeaders("Expires"); len(exps) > 0 {
		exp := exps[0]
		expSec, err := strconv.ParseInt(exp.Value(), 10, 32)
		if err != nil {
			slog.Error("parse expires header error", "error", err.Error())
			return
		}
		if expSec == 0 {
			isUnregister = true
		}
	} else {
		slog.Error("empty expires header")
		return
	}

	if isUnregister {
		DM.RemoveDevice(id)
		slog.Warn("Device unregistered", "device_id", id)
		return
	} else {
		if d, ok := DM.GetDevice(id); !ok {
			DM.AddDevice(id, &DeviceInfo{
				DeviceID:    id,
				SourceAddr:  req.Source(),
				NetworkType: req.Transport(),
			})
			s.respondRegister(req, http.StatusOK, "OK", tx)
			slog.Info(fmt.Sprintf("Register success %s %s", id, req.Source()))

			go s.ConfigDownload(id)
			go s.Catalog(id)
		} else {
			if d.SourceAddr != "" && !s.isSameIP(d.SourceAddr, req.Source()) {
				slog.Error("Device already registered", "device_id", id, "old_source", d.SourceAddr, "new_source", req.Source())
				// TODO: 如果ID重复，应采用虚拟ID
				s.respondRegister(req, http.StatusBadRequest, "Conflict Device ID", tx)
			} else {
				d.SourceAddr = req.Source()
				d.NetworkType = req.Transport()
				DM.UpdateDevice(id, d)
				s.respondRegister(req, http.StatusOK, "OK", tx)

				slog.Info(fmt.Sprintf("Re-register success %s %s", id, req.Source()))
			}
		}
	}
}

func (s *UAS) respondRegister(req *sip.Request, code sip.StatusCode, reason string, tx sip.ServerTransaction) {
	res := stack.NewRegisterResponse(req, code, reason)
	_ = tx.Respond(res)

}

func (s *UAS) onMessage(req *sip.Request, tx sip.ServerTransaction) {
	id := req.From().Address.User
	if len(id) != 20 {
		slog.Error("invalid device ID", "request", req.String())
	}

	slog.Debug(fmt.Sprintf("Received MESSAGE %s", req.String()))

	temp := &models.XmlMessageInfo{}
	decoder := xml.NewDecoder(bytes.NewReader([]byte(req.Body())))
	decoder.CharsetReader = charset.NewReaderLabel
	if err := decoder.Decode(temp); err != nil {
		slog.Error("decode message error", "error", err.Error(), "message", req.Body())
	}

	slog.Info(fmt.Sprintf("Received MESSAGE %s %s %s", temp.CmdType, temp.DeviceID, req.Source()))

	var body string
	switch temp.CmdType {
	case "Keepalive":
		if d, ok := DM.GetDevice(temp.DeviceID); ok && d.Online {
			// 更新设备心跳时间
			DM.UpdateDeviceHeartbeat(temp.DeviceID)
		} else {
			tx.Respond(sip.NewResponseFromRequest(req, http.StatusBadRequest, "", nil))
			return
		}
	case "SensorCatalog": // 兼容宇视，非国标
	case "Catalog":
		DM.UpdateChannels(temp.DeviceID, temp.DeviceList...)
		//go s.AutoInvite(temp.DeviceID, temp.DeviceList...)
	case "ConfigDownload":
		DM.UpdateDeviceConfig(temp.DeviceID, &temp.BasicParam)
	case "Alarm":
		slog.Info("Alarm")
	case "RecordInfo":
		// 从 recordQueryResults 中获取对应通道的结果通道
		if ch, ok := s.recordQueryResults.Load(temp.DeviceID); ok {
			// 发送查询结果
			resultChan := ch.(chan *models.XmlMessageInfo)
			resultChan <- temp
		}
	default:
		slog.Warn("Not supported CmdType", "cmd_type", temp.CmdType)
		response := sip.NewResponseFromRequest(req, http.StatusBadRequest, "", nil)
		tx.Respond(response)
		return
	}
	tx.Respond(sip.NewResponseFromRequest(req, http.StatusOK, "OK", []byte(body)))
}

func (s *UAS) onNotify(req *sip.Request, tx sip.ServerTransaction) {
	slog.Debug(fmt.Sprintf("Received NOTIFY %s", req.String()))
	tx.Respond(sip.NewResponseFromRequest(req, http.StatusOK, "OK", nil))
}
