package common

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"net"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	logmessage "github.com/perfect-panel/server/internal/model/logmessage"
	"github.com/perfect-panel/server/internal/svc"
	"github.com/perfect-panel/server/internal/types"
	"github.com/perfect-panel/server/pkg/logger"
	"github.com/perfect-panel/server/pkg/xerr"
	"github.com/pkg/errors"
)

type ReportLogMessageLogic struct {
	logger.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewReportLogMessageLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ReportLogMessageLogic {
	return &ReportLogMessageLogic{Logger: logger.WithContext(ctx), ctx: ctx, svcCtx: svcCtx}
}

func (l *ReportLogMessageLogic) ReportLogMessage(req *types.ReportLogMessageRequest, c *gin.Context) (resp *types.ReportLogMessageResponse, err error) {
	ip := clientIP(c)
	ua := c.GetHeader("User-Agent")
	locale := c.GetHeader("Accept-Language")

	// 简单限流：设备ID优先，其次IP
	limitKey := "logmsg:" + strings.TrimSpace(req.DeviceId)
	if limitKey == "logmsg:" {
		limitKey = "logmsg:" + ip
	}
	count, _ := l.svcCtx.Redis.Incr(l.ctx, limitKey).Result()
	if count == 1 {
		_ = l.svcCtx.Redis.Expire(l.ctx, limitKey, 60*time.Second).Err()
	}
	if count > 120 { // 每分钟最多120条
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.TooManyRequests), "too many reports")
	}

	// 指纹生成
	h := sha256.New()
	h.Write([]byte(strings.Join([]string{req.Message, req.Stack, req.ErrorCode, req.AppVersion, req.Platform}, "|")))
	digest := hex.EncodeToString(h.Sum(nil))

	var ctxStr string
	if req.Context != nil {
		if b, e := json.Marshal(req.Context); e == nil {
			ctxStr = string(b)
		}
	}
	var occurredAt *time.Time
	if req.OccurredAt > 0 {
		t := time.UnixMilli(req.OccurredAt)
		occurredAt = &t
	}

	var userIdPtr *int64
	if req.UserId > 0 {
		userIdPtr = &req.UserId
	}

	row := &logmessage.LogMessage{
		Platform:   safeTruncate(req.Platform, 32),
		AppVersion: safeTruncate(req.AppVersion, 64),
		OsName:     safeTruncate(req.OsName, 64),
		OsVersion:  safeTruncate(req.OsVersion, 64),
		DeviceId:   safeTruncate(req.DeviceId, 255),
		UserId:     userIdPtr,
		SessionId:  safeTruncate(req.SessionId, 255),
		Level:      req.Level,
		ErrorCode:  safeTruncate(req.ErrorCode, 128),
		Message:    safeTruncate(req.Message, 1024*64),
		Stack:      safeTruncate(req.Stack, 1024*1024),
		Context:    ctxStr,
		ClientIP:   ip,
		UserAgent:  safeTruncate(ua, 255),
		Locale:     safeTruncate(locale, 16),
		Digest:     digest,
		OccurredAt: occurredAt,
	}

	if err = l.svcCtx.LogMessageModel.Insert(l.ctx, row); err != nil {
		// 唯一指纹冲突时尝试查询已有记录返回ID
		ex, _, findErr := l.svcCtx.LogMessageModel.Filter(l.ctx, &logmessage.FilterParams{Keyword: req.Message, Page: 1, Size: 1})
		if findErr == nil && len(ex) > 0 {
			return &types.ReportLogMessageResponse{Id: ex[0].Id}, nil
		}
		l.Errorf("[ReportLogMessage] insert error: %v", err)
		return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "insert log_message failed: %v", err)
	}
	return &types.ReportLogMessageResponse{Id: row.Id}, nil
}

func safeTruncate(s string, n int) string {
	if len(s) <= n {
		return s
	}
	return s[:n]
}

func clientIP(c *gin.Context) string {
	ip := c.ClientIP()
	if ip != "" {
		return ip
	}
	host, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr))
	if err == nil && host != "" {
		return host
	}
	return ""
}
