package usecase

import (
	"context"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/samber/lo"

	"github.com/chaitin/panda-wiki/consts"
	"github.com/chaitin/panda-wiki/domain"
	"github.com/chaitin/panda-wiki/log"
	"github.com/chaitin/panda-wiki/repo/ipdb"
	"github.com/chaitin/panda-wiki/repo/pg"
)

type CommentUsecase struct {
	logger      *log.Logger
	CommentRepo *pg.CommentRepository
	NodeRepo    *pg.NodeRepository
	ipRepo      *ipdb.IPAddressRepo
	authRepo    *pg.AuthRepo
}

func NewCommentUsecase(commentRepo *pg.CommentRepository, logger *log.Logger,
	nodeRepo *pg.NodeRepository, ipRepo *ipdb.IPAddressRepo, authRepo *pg.AuthRepo) *CommentUsecase {
	return &CommentUsecase{
		logger:      logger.WithModule("usecase.comment"),
		CommentRepo: commentRepo,
		NodeRepo:    nodeRepo,
		ipRepo:      ipRepo,
		authRepo:    authRepo,
	}
}

func (u *CommentUsecase) CreateComment(ctx context.Context, commentReq *domain.CommentReq, KbID string, remoteIP string,
	status domain.CommentStatus, userID uint) (string, error) {
	// node
	if _, err := u.NodeRepo.GetNodeByID(ctx, commentReq.NodeID); err != nil {
		return "", err
	}

	// 构造结构体给下方数据库进行插入
	CommentID, err := uuid.NewV7()
	if err != nil {
		return "", err
	}
	CommentStr := CommentID.String()

	err = u.CommentRepo.CreateComment(ctx, &domain.Comment{
		ID:     CommentStr,
		NodeID: commentReq.NodeID,
		Info: domain.CommentInfo{
			UserName:   commentReq.UserName,
			RemoteIP:   remoteIP,
			AuthUserID: userID, // default = 0. have no auth info
		},
		ParentID:  commentReq.ParentID,
		RootID:    commentReq.RootID,
		Content:   commentReq.Content,
		CreatedAt: time.Now(),
		KbID:      KbID,
		Status:    status,
	})
	if err != nil {
		return "", err
	}

	// success
	return CommentStr, nil
}

func (u *CommentUsecase) GetCommentListByNodeID(ctx context.Context, nodeID string, edition consts.LicenseEdition) (*domain.PaginatedResult[[]*domain.ShareCommentListItem], error) {
	comments, total, err := u.CommentRepo.GetCommentList(ctx, nodeID, edition)
	if err != nil {
		return nil, err
	}
	// get auth userinfo --> auth_user_id is not 0
	authIDs := make([]uint, 0, len(comments))
	for _, comment := range comments {
		if comment.Info.AuthUserID != 0 {
			authIDs = append(authIDs, comment.Info.AuthUserID)
		}
	}
	// get user info according authIDs
	authMap, err := u.authRepo.GetAuthUserinfoByIDs(ctx, authIDs)
	if err != nil {
		u.logger.Error("get user info failed", log.Error(err))
	}

	// get ip address
	ipAddressMap := make(map[string]*domain.IPAddress)
	lo.Map(comments, func(comment *domain.ShareCommentListItem, _ int) *domain.ShareCommentListItem {
		if _, ok := ipAddressMap[comment.Info.RemoteIP]; !ok {
			ipAddress, err := u.ipRepo.GetIPAddress(ctx, comment.Info.RemoteIP)
			if err != nil {
				u.logger.Error("get ip address failed", log.Error(err), log.String("ip", comment.Info.RemoteIP))
				return comment
			}
			ipAddressMap[comment.Info.RemoteIP] = ipAddress
			comment.IPAddress = ipAddress
			comment.Info.RemoteIP = maskIP(comment.Info.RemoteIP)
			comment.IPAddress.IP = maskIP(comment.IPAddress.IP)
		} else {
			comment.IPAddress = ipAddressMap[comment.Info.RemoteIP]
		}
		if _, ok := authMap[comment.Info.AuthUserID]; ok { // moderate userinfo
			comment.Info.UserName = authMap[comment.Info.AuthUserID].AuthUserInfo.Username
			comment.Info.Avatar = authMap[comment.Info.AuthUserID].AuthUserInfo.AvatarUrl
			comment.Info.Email = authMap[comment.Info.AuthUserID].AuthUserInfo.Email
		}
		return comment
	})
	// success
	return domain.NewPaginatedResult(comments, uint64(total)), nil
}

func (u *CommentUsecase) GetCommentListByKbID(ctx context.Context, req *domain.CommentListReq, edition consts.LicenseEdition) (*domain.PaginatedResult[[]*domain.CommentListItem], error) {
	comments, total, err := u.CommentRepo.GetCommentListByKbID(ctx, req, edition)
	if err != nil {
		return nil, err
	}
	// get auth userinfo --> auth_user_id is not 0
	authIDs := make([]uint, 0, len(comments))
	for _, comment := range comments {
		if comment.Info.AuthUserID != 0 {
			authIDs = append(authIDs, comment.Info.AuthUserID)
		}
	}
	// get user info according authIDs
	authMap, err := u.authRepo.GetAuthUserinfoByIDs(ctx, authIDs)
	if err != nil {
		u.logger.Error("get user info failed", log.Error(err))
	}
	// get ip address
	ipAddressMap := make(map[string]*domain.IPAddress)
	lo.Map(comments, func(comment *domain.CommentListItem, _ int) *domain.CommentListItem {
		if _, ok := ipAddressMap[comment.Info.RemoteIP]; !ok {
			ipAddress, err := u.ipRepo.GetIPAddress(ctx, comment.Info.RemoteIP)
			if err != nil {
				u.logger.Error("get ip address failed", log.Error(err), log.String("ip", comment.Info.RemoteIP))
				return comment
			}
			ipAddressMap[comment.Info.RemoteIP] = ipAddress
			comment.IPAddress = ipAddress
		} else {
			comment.IPAddress = ipAddressMap[comment.Info.RemoteIP]
		}
		if _, ok := authMap[comment.Info.AuthUserID]; ok { // moderate userinfo
			comment.Info.UserName = authMap[comment.Info.AuthUserID].AuthUserInfo.Username
			comment.Info.Avatar = authMap[comment.Info.AuthUserID].AuthUserInfo.AvatarUrl
			comment.Info.Email = authMap[comment.Info.AuthUserID].AuthUserInfo.Email
		}
		return comment
	})

	return domain.NewPaginatedResult(comments, uint64(total)), nil
}

// 批量删除评论， （简单化，只删除传入评论id）
func (u *CommentUsecase) DeleteCommentList(ctx context.Context, req *domain.DeleteCommentListReq) error {
	err := u.CommentRepo.DeleteCommentList(ctx, req.IDS)
	if err != nil {
		return err
	}
	return nil
}

func maskIP(ip string) string {
	if ip == "" {
		return ""
	}
	// 处理 IPv4 地址 (格式: a.b.c.d)
	if strings.Contains(ip, ".") {
		parts := strings.Split(ip, ".")
		if len(parts) != 4 { // 非标准IPv4格式直接返回原值
			return ""
		}
		return parts[0] + ".*.*." + parts[3]
	}
	// 处理 IPv6 地址 (标准格式包含冒号)
	if strings.Contains(ip, ":") {
		return ""
	}

	return ""
}
