package security

import (
	"fmt"
	"strconv"

	"github.com/silenceper/wechat/v2/miniprogram/context"
	"github.com/silenceper/wechat/v2/util"
)

const (
	mediaCheckAsyncURL = "https://api.weixin.qq.com/wxa/media_check_async?access_token=%s"
	imageCheckURL      = "https://api.weixin.qq.com/wxa/img_sec_check?access_token=%s"
	msgCheckURL        = "https://api.weixin.qq.com/wxa/msg_sec_check?access_token=%s"
)

// Security 内容安全
type Security struct {
	*context.Context
}

// NewSecurity init
func NewSecurity(ctx *context.Context) *Security {
	return &Security{ctx}
}

// MediaCheckAsyncV1Request 图片/音频异步校验请求参数
type MediaCheckAsyncV1Request struct {
	MediaURL  string `json:"media_url"`  // 要检测的图片或音频的url，支持图片格式包括jpg, jepg, png, bmp, gif（取首帧），支持的音频格式包括mp3, aac, ac3, wma, flac, vorbis, opus, wav
	MediaType uint8  `json:"media_type"` // 1:音频;2:图片
}

// MediaCheckAsyncV1 异步校验图片/音频是否含有违法违规内容
// Deprecated
// 在2021年9月1日停止更新，请尽快更新至 2.0 接口。建议使用 MediaCheckAsync
func (security *Security) MediaCheckAsyncV1(in *MediaCheckAsyncV1Request) (traceID string, err error) {
	accessToken, err := security.GetAccessToken()
	if err != nil {
		return
	}

	uri := fmt.Sprintf(mediaCheckAsyncURL, accessToken)
	response, err := util.PostJSON(uri, in)
	if err != nil {
		return
	}

	// 使用通用方法返回错误
	var res struct {
		util.CommonError
		TraceID string `json:"trace_id"`
	}
	err = util.DecodeWithError(response, &res, "MediaCheckAsyncV1")
	if err != nil {
		return
	}

	traceID = res.TraceID
	return
}

// MediaCheckAsyncRequest 图片/音频异步校验请求参数
type MediaCheckAsyncRequest struct {
	MediaURL  string `json:"media_url"`  // 要检测的图片或音频的url，支持图片格式包括jpg, jepg, png, bmp, gif（取首帧），支持的音频格式包括mp3, aac, ac3, wma, flac, vorbis, opus, wav
	MediaType uint8  `json:"media_type"` // 1:音频;2:图片
	OpenID    string `json:"openid"`     // 用户的openid（用户需在近两小时访问过小程序）
	Scene     uint8  `json:"scene"`      // 场景枚举值（1 资料；2 评论；3 论坛；4 社交日志）
}

// MediaCheckAsync 异步校验图片/音频是否含有违法违规内容
func (security *Security) MediaCheckAsync(in *MediaCheckAsyncRequest) (traceID string, err error) {
	accessToken, err := security.GetAccessToken()
	if err != nil {
		return
	}

	var req struct {
		MediaCheckAsyncRequest
		Version uint `json:"version"` // 接口版本号，2.0版本为固定值2
	}
	req.MediaCheckAsyncRequest = *in
	req.Version = 2

	uri := fmt.Sprintf(mediaCheckAsyncURL, accessToken)
	response, err := util.PostJSON(uri, req)
	if err != nil {
		return
	}

	// 使用通用方法返回错误
	var res struct {
		util.CommonError
		TraceID string `json:"trace_id"`
	}
	err = util.DecodeWithError(response, &res, "MediaCheckAsync")
	if err != nil {
		return
	}

	traceID = res.TraceID
	return
}

// ImageCheckV1 校验一张图片是否含有违法违规内容（同步）
// https://developers.weixin.qq.com/miniprogram/dev/framework/security.imgSecCheck.html
// Deprecated
// 在2021年9月1日停止更新。建议使用 MediaCheckAsync
func (security *Security) ImageCheckV1(filename string) (err error) {
	accessToken, err := security.GetAccessToken()
	if err != nil {
		return
	}

	uri := fmt.Sprintf(imageCheckURL, accessToken)
	response, err := util.PostFile("media", filename, uri)
	if err != nil {
		return
	}

	// 使用通用方法返回错误
	return util.DecodeWithCommonError(response, "ImageCheckV1")
}

// CheckSuggest 检查建议
type CheckSuggest string

const (
	// CheckSuggestRisky 违规风险建议
	CheckSuggestRisky CheckSuggest = "risky"
	// CheckSuggestPass 安全
	CheckSuggestPass CheckSuggest = "pass"
	// CheckSuggestReview 需要审查
	CheckSuggestReview CheckSuggest = "review"
)

// MsgScene 文本场景
type MsgScene uint8

const (
	// MsgSceneMaterial 资料文件检查场景
	MsgSceneMaterial MsgScene = iota + 1
	// MsgSceneComment 评论
	MsgSceneComment
	// MsgSceneForum 论坛
	MsgSceneForum
	// MsgSceneSocialLog 社交日志
	MsgSceneSocialLog
)

// CheckLabel 检查命中标签
type CheckLabel int

func (cl CheckLabel) String() string {
	switch cl {
	case 100:
		return "正常"
	case 10001:
		return "广告"
	case 20001:
		return "时政"
	case 20002:
		return "色情"
	case 20003:
		return "辱骂"
	case 20006:
		return "违法犯罪"
	case 20008:
		return "欺诈"
	case 20012:
		return "低俗"
	case 20013:
		return "版权"
	case 21000:
		return "其他"
	default:
		return strconv.Itoa(int(cl))
	}
}

// MsgCheckRequest 文本检查请求
type MsgCheckRequest struct {
	OpenID    string   `json:"openid"`    // 用户的openid（用户需在近两小时访问过小程序）
	Scene     MsgScene `json:"scene"`     // 场景枚举值（1 资料；2 评论；3 论坛；4 社交日志）
	Content   string   `json:"content"`   // 需检测的文本内容，文本字数的上限为 2500 字，需使用 UTF-8 编码
	Nickname  string   `json:"nickname"`  // （非必填）用户昵称，需使用UTF-8编码
	Title     string   `json:"title"`     // （非必填）文本标题，需使用UTF-8编码
	Signature string   `json:"signature"` // （非必填）个性签名，该参数仅在资料类场景有效(scene=1)，需使用UTF-8编码
}

// MsgCheckResponse 文本检查响应
type MsgCheckResponse struct {
	util.CommonError
	TraceID string `json:"trace_id"` // 唯一请求标识
	Result  struct {
		Suggest CheckSuggest `json:"suggest"` // 建议
		Label   CheckLabel   `json:"label"`   // 命中标签
	} `json:"result"` // 综合结果
	Detail []struct {
		ErrCode  int64      `json:"errcode"`  // 错误码，仅当该值为0时，该项结果有效
		Strategy string     `json:"strategy"` // 策略类型
		Suggest  string     `json:"suggest"`  // 建议
		Label    CheckLabel `json:"label"`    // 命中标签
		Prob     uint       `json:"prob"`     // 置信度。0-100，越高代表越有可能属于当前返回的标签（label）
		Keyword  string     `json:"keyword"`  // 命中的自定义关键词
	} `json:"detail"` // 详细检测结果
}

// MsgCheckV1 检查一段文本是否含有违法违规内容
// Deprecated
// 在2021年9月1日停止更新，请尽快更新至 2.0 接口。建议使用 MsgCheck
func (security *Security) MsgCheckV1(content string) (res MsgCheckResponse, err error) {
	accessToken, err := security.GetAccessToken()
	if err != nil {
		return
	}

	var req struct {
		Content string `json:"content"`
	}
	req.Content = content

	uri := fmt.Sprintf(msgCheckURL, accessToken)
	response, err := util.PostJSON(uri, req)
	if err != nil {
		return
	}

	// 使用通用方法返回错误
	err = util.DecodeWithError(response, &res, "security.MsgCheckV1")
	return
}

// MsgCheck 检查一段文本是否含有违法违规内容
func (security *Security) MsgCheck(in *MsgCheckRequest) (res MsgCheckResponse, err error) {
	accessToken, err := security.GetAccessToken()
	if err != nil {
		return
	}

	var req struct {
		MsgCheckRequest
		Version uint `json:"version"`
	}
	req.MsgCheckRequest = *in
	req.Version = 2

	uri := fmt.Sprintf(msgCheckURL, accessToken)
	response, err := util.PostJSON(uri, req)
	if err != nil {
		return
	}

	// 使用通用方法返回错误
	err = util.DecodeWithError(response, &res, "security.MsgCheck")
	return
}
