// Copyright © 2023 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package msg

import (
	"context"
	"encoding/json"
	"fmt"
	_ "image/jpeg"
	_ "image/png"
	"io"
	"net/http"
	"os"
	"time"

	"git.imall.cloud/openim/open-im-server-deploy/pkg/common/servererrs"
	"git.imall.cloud/openim/protocol/constant"
	"git.imall.cloud/openim/protocol/sdkws"
	"github.com/openimsdk/tools/errs"
	"github.com/openimsdk/tools/log"
)

// PictureElem 用于解析图片消息内容
type PictureElem struct {
	SourcePicture struct {
		URL string `json:"url"`
	} `json:"sourcePicture"`
	BigPicture struct {
		URL string `json:"url"`
	} `json:"bigPicture"`
	SnapshotPicture struct {
		URL string `json:"url"`
	} `json:"snapshotPicture"`
}

// checkImageContainsQRCode 检测图片中是否包含二维码
// userType: 0-普通用户（不能发送包含二维码的图片），1-特殊用户（可以发送）
func (m *msgServer) checkImageContainsQRCode(ctx context.Context, msgData *sdkws.MsgData, userType int32) error {
	// userType=1 的用户可以发送包含二维码的图片，不进行检测
	if userType == 1 {
		return nil
	}

	// 只检测图片类型的消息
	if msgData.ContentType != constant.Picture {
		return nil
	}

	// 解析图片消息内容
	var pictureElem PictureElem
	if err := json.Unmarshal(msgData.Content, &pictureElem); err != nil {
		// 如果解析失败，记录警告但不拦截
		log.ZWarn(ctx, "failed to parse picture message", err, "content", string(msgData.Content))
		return nil
	}

	// 获取图片URL（优先使用原图，如果没有则使用大图）
	imageURL := pictureElem.SourcePicture.URL
	if imageURL == "" {
		imageURL = pictureElem.BigPicture.URL
	}
	if imageURL == "" {
		imageURL = pictureElem.SnapshotPicture.URL
	}
	if imageURL == "" {
		// 没有有效的图片URL，无法检测
		log.ZWarn(ctx, "no valid image URL found in picture message", nil, "pictureElem", pictureElem)
		return nil
	}

	// 下载图片并检测二维码
	hasQRCode, err := m.detectQRCodeInImage(ctx, imageURL, "")
	if err != nil {
		// 检测失败时，记录错误但不拦截（避免误拦截）
		log.ZWarn(ctx, "QR code detection failed", err, "imageURL", imageURL)
		return nil
	}

	if hasQRCode {
		log.ZWarn(ctx, "检测到二维码，拒绝发送", nil, "imageURL", imageURL, "userType", userType)
		return servererrs.ErrImageContainsQRCode.WrapMsg("userType=0的用户不能发送包含二维码的图片")
	}

	return nil
}

// detectQRCodeInImage 下载图片并检测是否包含二维码
func (m *msgServer) detectQRCodeInImage(ctx context.Context, imageURL string, logPrefix string) (bool, error) {
	// 创建带超时的HTTP客户端
	client := &http.Client{
		Timeout: 5 * time.Second,
	}

	// 下载图片
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, imageURL, nil)
	if err != nil {
		log.ZError(ctx, "创建HTTP请求失败", err, "imageURL", imageURL)
		return false, errs.WrapMsg(err, "failed to create request")
	}

	resp, err := client.Do(req)
	if err != nil {
		log.ZError(ctx, "下载图片失败", err, "imageURL", imageURL)
		return false, errs.WrapMsg(err, "failed to download image")
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		log.ZError(ctx, "下载图片状态码异常", nil, "statusCode", resp.StatusCode, "imageURL", imageURL)
		return false, errs.WrapMsg(fmt.Errorf("unexpected status code: %d", resp.StatusCode), "failed to download image")
	}

	// 限制图片大小（最大10MB）
	const maxImageSize = 10 * 1024 * 1024
	limitedReader := io.LimitReader(resp.Body, maxImageSize+1)

	// 创建临时文件
	tmpFile, err := os.CreateTemp("", "qrcode_detect_*.tmp")
	if err != nil {
		log.ZError(ctx, "创建临时文件失败", err)
		return false, errs.WrapMsg(err, "failed to create temp file")
	}
	tmpFilePath := tmpFile.Name()

	// 确保检测完成后删除临时文件（无论成功还是失败）
	defer func() {
		// 确保文件已关闭后再删除
		if tmpFile != nil {
			_ = tmpFile.Close()
		}
		// 删除临时文件，忽略文件不存在的错误
		if err := os.Remove(tmpFilePath); err != nil && !os.IsNotExist(err) {
			log.ZWarn(ctx, "删除临时文件失败", err, "tmpFile", tmpFilePath)
		}
	}()

	// 保存图片到临时文件
	written, err := io.Copy(tmpFile, limitedReader)
	if err != nil {
		log.ZError(ctx, "保存图片到临时文件失败", err, "tmpFile", tmpFilePath)
		return false, errs.WrapMsg(err, "failed to save image")
	}

	// 关闭文件以便后续读取
	if err := tmpFile.Close(); err != nil {
		log.ZError(ctx, "关闭临时文件失败", err, "tmpFile", tmpFilePath)
		return false, errs.WrapMsg(err, "failed to close temp file")
	}

	// 检查文件大小
	if written > maxImageSize {
		log.ZWarn(ctx, "图片过大", nil, "size", written, "maxSize", maxImageSize)
		return false, errs.WrapMsg(fmt.Errorf("image too large: %d bytes", written), "image size exceeds limit")
	}

	// 使用优化的并行解码器检测二维码
	hasQRCode, err := m.detectQRCodeWithDecoder(ctx, tmpFilePath, "")
	if err != nil {
		log.ZError(ctx, "二维码检测失败", err, "tmpFile", tmpFilePath)
		return false, err
	}

	return hasQRCode, nil
}

// detectQRCodeWithDecoder 使用优化的解码器检测二维码
func (m *msgServer) detectQRCodeWithDecoder(ctx context.Context, imagePath string, logPrefix string) (bool, error) {
	// 使用Custom解码器（已移除Quirc解码器依赖）
	customDecoder := &CustomQRDecoder{}

	// 执行解码
	hasQRCode, err := customDecoder.Decode(ctx, imagePath, logPrefix)
	if err != nil {
		log.ZError(ctx, "解码器检测失败", err, "decoder", customDecoder.Name())
		return false, err
	}

	return hasQRCode, nil
}
