package com.cyksj.service.mini.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cyksj.common.exception.BusinessRuntimeException;
import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
import com.cyksj.common.util.Jsons;
import com.cyksj.common.util.SignatureUtils;
import com.cyksj.common.util.StringUtil;
import com.cyksj.mapper.mini.*;
import com.cyksj.model.entity.*;
import com.cyksj.model.request.MiniAiPicturesReq;
import com.cyksj.model.request.MiniPopularizeIncrTimesReq;
import com.cyksj.model.response.MiniPopularizeResp;
import com.cyksj.redis.RedisService;
import com.cyksj.server.listener.ConsoleStreamListener;
import com.cyksj.service.mini.MiniPopularizeCommonService;
import com.cyksj.service.mini.MiniPopularizeFrontService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okhttp3.sse.EventSource;
import okhttp3.sse.EventSources;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.stream.FileImageOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 项目名: yhlxj2
 * 文件名: MiniPopularizeFrontServiceImpl
 * 创建者: JavaZou
 * 创建时间:2025/4/8 16:51
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class MiniPopularizeFrontServiceImpl implements MiniPopularizeFrontService {
	public static final String GPT_URL = "https://api.nf.video/v1/chat/completions";

	public static final String NEW_MODEL_GPT_URL = "https://api.nf.video/v1/images/generations";

	public static final String NEW_MODEL_GPT_EDIT_URL = "https://api.nf.video/v1/images/edits";

	public static final String GPT_DRAW_TOKEN = "sk-Tsxxyfly6RCuyJ6hq6iNdPkSD1SzUxQX3mgO0ItF1AaLWacR";

	public static final String SIGN_SECRET = "6mR7XvKbPeShVmYq3t5w9z$C&FH@McQ";

	private final MiniPopularizeUserConversationMapper conversationMapper;

	private final MiniPopularizeUserMapper miniPopularizeUserMapper;

	private final RedisService redisService;

	private final MiniPopularizeCommonService miniPopularizeCommonService;

	private final MiniPopularizeConfigMapper miniPopularizeConfigMapper;

	private final MiniPopularizeUserConversationChatMapper conversationChatMapper;

	private final MiniPopularizeConfigUseRecordMapper miniPopularizeConfigUseRecordMapper;

	private final MiniPopularizeUserIncrTimesRecordMapper miniPopularizeUserIncrTimesRecordMapper;

	private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();

	/**
	 * 连接超时
	 */
	private static final long TIMEOUT = 90;

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public MiniPopularizeResp createPictures(MiniAiPicturesReq miniAiPicturesReq) throws Exception {
		String conversationId = miniAiPicturesReq.getConversationId();
		AtomicBoolean recoverNum = new AtomicBoolean(true);
		Long userId = miniAiPicturesReq.getUserId();
		conversationId = checkUser(MiniPopularizeUserConversation.builder()
				.userId(userId)
				.content(miniAiPicturesReq.getTips())
				.build(), conversationId, recoverNum);
		miniAiPicturesReq.setConversationId(conversationId);
		Long configId = miniAiPicturesReq.getId();
		if (configId != null) {
			MiniPopularizeConfig miniPopularizeConfig = miniPopularizeConfigMapper.selectById(miniAiPicturesReq.getId());
			if (miniPopularizeConfig != null) {
				//垫图
				if (miniPopularizeConfig.getType() == MiniPopularizeConfig.Type.Style || miniPopularizeConfig.getType() == MiniPopularizeConfig.Type.Banner) {
					miniAiPicturesReq.setType(2);
				}
				miniAiPicturesReq.setTips(miniPopularizeConfig.getTips());
			}
		}
		int model = miniAiPicturesReq.getModel();
		//聊天记录
		MiniPopularizeUserConversationChat chat = MiniPopularizeUserConversationChat.builder()
				.conversationId(conversationId)
				.content(miniAiPicturesReq.getTips())
				.img(miniAiPicturesReq.getImg())
				.type(MiniPopularizeUserConversationChat.Type.text)
				.role(MiniPopularizeUserConversationChat.Role.user)
				.cid(configId)
				.userId(userId)
				.build();
		conversationChatMapper.insert(chat);
		//图像生成记录
		MiniPopularizeUserConversationChat imageRecord = MiniPopularizeUserConversationChat.builder()
				.userId(miniAiPicturesReq.getUserId())
				.role(MiniPopularizeUserConversationChat.Role.assistant)
				.conversationId(miniAiPicturesReq.getConversationId())
				.type(MiniPopularizeUserConversationChat.Type.image)
				.status(MiniPopularizeUserConversationChat.Status.processing)
				.build();
		miniPopularizeCommonService.saveImgRecord(imageRecord);
		//新版模型
		if (model == 0) {
			//只要上传了图片 就是垫图
			if (StrUtil.isNotEmpty(miniAiPicturesReq.getImg())) {
				miniAiPicturesReq.setType(2);
			}
			newGptModelCreateImage(miniAiPicturesReq, imageRecord);
		} else {
			ConsoleStreamListener listener = ConsoleStreamListener.builder()
					.userId(userId)
					.miniPopularizeCommonService(miniPopularizeCommonService)
					.conversationId(conversationId)
					.imgRecordId(imageRecord.getId())
					.recoverNum(recoverNum.get())
					.build();
			streamChatCompletion(miniAiPicturesReq, listener);
		}
		MiniPopularizeResp resp = MiniPopularizeResp.builder()
				.conversationId(conversationId)
				.chatId(imageRecord.getId())
				.build();
		return resp;
	}

	@Override
	public MiniPopularizeUser getUser(long userId) {
		MiniPopularizeUser user = miniPopularizeUserMapper.selectOne(Wrappers.lambdaQuery(MiniPopularizeUser.class)
				.eq(MiniPopularizeUser::getUserId, userId).last("limit 1").select(MiniPopularizeUser::getNum, MiniPopularizeUser::getCloseTc));
		if (user != null) {
			Object num = redisService.get(RedisService.key.TG_MINI_USER_SAHRE_COUNT_KEY.getName() + DateTime.now().toDateStr() + ":" + userId);
			if (num != null) {
				user.setShareCount(Integer.parseInt(num.toString()));
			}
		}
		return user;
	}

	@Override
	public MiniPopularizeUserConversationChat getImgResultById(long userId, Long imageId) {
		MiniPopularizeUserConversationChat miniPopularizeUserConversationChat = conversationChatMapper.selectOne(Wrappers.lambdaQuery(MiniPopularizeUserConversationChat.class)
				.eq(MiniPopularizeUserConversationChat::getUserId, userId)
				.eq(MiniPopularizeUserConversationChat::getId, imageId)
				.last("limit 1"));
		return miniPopularizeUserConversationChat;
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void incrGptTimes(MiniPopularizeIncrTimesReq req) throws Exception {
		Long userId = req.getUserId();
		MiniPopularizeUser user = miniPopularizeUserMapper.selectOne(Wrappers.lambdaQuery(MiniPopularizeUser.class)
				.eq(MiniPopularizeUser::getUserId, userId).last("limit 1"));
		if (user == null) {
			throw BusinessRuntimeException.getInstance("系统错误，用户不存在");
		}
		String sign = req.getSign();
		if (StrUtil.isEmpty(sign)) {
			throw BusinessRuntimeException.getInstance("系统错误");
		}
		Long timestamp = req.getTimestamp();
		String bsign = SignatureUtils.generateSignature(Map.of("timestamp", timestamp.toString()), SIGN_SECRET);
		if (!StrUtil.equals(bsign, sign)) {
			throw BusinessRuntimeException.getInstance("系统错误");
		}
		//检验分享次数
		shareCountCheck(userId);
		//1次
		Integer addNum = BigDecimal.ONE.intValue();
		MiniPopularizeUserIncrTimesRecord miniPopularizeUserIncrTimesRecord = new MiniPopularizeUserIncrTimesRecord();
		miniPopularizeUserIncrTimesRecord.setUserId(userId);
		miniPopularizeUserIncrTimesRecord.setSign(sign);
		miniPopularizeUserIncrTimesRecord.setNum(addNum);
		try {
			miniPopularizeUserIncrTimesRecordMapper.insert(miniPopularizeUserIncrTimesRecord);
			Integer oriNum = user.getNum();
			miniPopularizeCommonService.incrGptTimes(userId, oriNum, addNum);
		} catch (Exception e) {

		}
	}

	@Override
	public void closeTc(long userId) {
		Optional.ofNullable(miniPopularizeUserMapper.selectOne(Wrappers.lambdaQuery(MiniPopularizeUser.class)
						.eq(MiniPopularizeUser::getUserId, userId)
						.last("limit 1")))
				.ifPresent(user -> {
					if (!user.getCloseTc()) {
						user.setCloseTc(true);
						miniPopularizeUserMapper.updateById(user);
					}
				});
	}

	private String checkUser(MiniPopularizeUserConversation conversation, String conversationId, AtomicBoolean recoverNum) {
//		if (conversationId != null) {
//			MiniPopularizeUserConversationChat miniPopularizeUserConversationChat = conversationChatMapper.selectOne(Wrappers.lambdaQuery(MiniPopularizeUserConversationChat.class)
//					.eq(MiniPopularizeUserConversationChat::getConversationId, conversationId)
//					.eq(MiniPopularizeUserConversationChat::getType, MiniPopularizeUserConversationChat.Type.image)
//					.orderByDesc(MiniPopularizeUserConversationChat::getId)
//					.last("limit 1"));
//			if (miniPopularizeUserConversationChat == null || !miniPopularizeUserConversationChat.getUserId().equals(conversation.getUserId())) {
//				throw BusinessRuntimeException.getInstance("系统错误");
//			} else if (miniPopularizeUserConversationChat.getStatus() != MiniPopularizeUserConversationChat.Status.success) {
//				throw BusinessRuntimeException.getInstance("图片还未生成，无法进行修改");
//			}
//			recoverNum.set(true);
//		}
		Long userId = conversation.getUserId();
		//扣除次数
		decrMiniUserGptNum(userId);
		if (conversationId == null) {
			conversation.setConversationId(RandomUtil.randomString(32));
			conversationMapper.insert(conversation);
			conversationId = conversation.getConversationId();
		}
		return conversationId;
	}

	/**
	 * 流式输出
	 */
	public void streamChatCompletion(MiniAiPicturesReq miniAiPicturesReq,
	                                 ConsoleStreamListener eventSourceListener) {
		JSONObject json = new JSONObject();
		json.putOpt("model", "gpt-4o-image-vip");
		json.putOpt("stream", true);
		JSONArray messages = new JSONArray();
		String conversationId = miniAiPicturesReq.getConversationId();
		MiniPopularizeUserConversationChat miniPopularizeUserConversationChat = null;
		if (conversationId != null) {
			miniPopularizeUserConversationChat = conversationChatMapper.selectOne(Wrappers.lambdaQuery(MiniPopularizeUserConversationChat.class)
					.eq(MiniPopularizeUserConversationChat::getConversationId, conversationId)
					.eq(MiniPopularizeUserConversationChat::getStatus, MiniPopularizeUserConversationChat.Status.success)
					.last("limit 1"));
		}
		//原图上修改
		if (miniPopularizeUserConversationChat != null && miniPopularizeUserConversationChat.getStatus() != null && miniPopularizeUserConversationChat.getStatus() == MiniPopularizeUserConversationChat.Status.success) {
			//组装上下文
			contentMsg(messages, miniPopularizeUserConversationChat.getConversationId());
		} else {
			String img = miniAiPicturesReq.getImg();
			if (StrUtil.isNotBlank(img)) {
				JSONArray array = JSONUtil.createArray();
				JSONObject params = JSONUtil.createObj().set("role", "user").set("content", array.set(JSONUtil.createObj().set("type", "text").set("text", miniAiPicturesReq.getTips())));
				for (Object ct : JSONUtil.parseArray(img)) {
					array.set(JSONUtil.createObj().set("type", "image_url").set("image_url", JSONUtil.createObj().set("url", ct)));
				}
				messages.add(params);
			} else {
				messages.add(JSONUtil.createObj().set("role", "user").set("content", miniAiPicturesReq.getTips()));
			}
		}
		json.putOpt("messages", messages);
		try {
			EventSource.Factory factory = EventSources.createFactory(getOkHttpClient());
			ObjectMapper mapper = new ObjectMapper();
			String requestBody = mapper.writeValueAsString(json);
			Request request = new Request.Builder()
					.url(GPT_URL)
					.post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()),
							requestBody))
					.header(Header.AUTHORIZATION.name(), GPT_DRAW_TOKEN)
					.build();
			factory.newEventSource(request, eventSourceListener);
			if (miniAiPicturesReq.getId() != null) {
				recordConfigUseRecord(miniAiPicturesReq.getId(), miniAiPicturesReq.getUserId());
			}
		} catch (Exception e) {
			log.error("gpt create image请求出错：{}", StringUtil.getErrorText(e));
		}
	}

	private void contentMsg(JSONArray messages, String conversationId) {
		List<MiniPopularizeUserConversationChat> miniPopularizeUserConversationChats = conversationChatMapper.selectList(Wrappers.lambdaQuery(MiniPopularizeUserConversationChat.class)
				.eq(MiniPopularizeUserConversationChat::getConversationId, conversationId));
		miniPopularizeUserConversationChats.forEach(chat -> {
			if (chat.getStatus() != null && chat.getStatus() == MiniPopularizeUserConversationChat.Status.processing) {
				return;
			}
			if (chat.getRole() != null && chat.getRole() == MiniPopularizeUserConversationChat.Role.sys) {
				return;
			}
			String content = chat.getContent();
			if (chat.getRole() == MiniPopularizeUserConversationChat.Role.user) {
				messages.add(JSONUtil.createObj().set("role", "user").set("content", content));
				String img = chat.getImg();
				if (StrUtil.isNotBlank(img)) {
					if (img.contains("[")) {
						for (Object ct : JSONUtil.parseArray(img)) {
							messages.add(JSONUtil.createObj().set("role", "user").set("content", ct));
						}
					} else messages.add(JSONUtil.createObj().set("role", "user").set("content", img));
				}
			} else messages.add(JSONUtil.createObj().set("role", "assistant").set("content", content));
		});
	}

	public OkHttpClient getOkHttpClient() {
		OkHttpClient.Builder client = new OkHttpClient.Builder();
		client.connectTimeout(TIMEOUT, TimeUnit.SECONDS);
		client.writeTimeout(TIMEOUT, TimeUnit.SECONDS);
		client.readTimeout(TIMEOUT, TimeUnit.SECONDS);
		return client.build();
	}

	public void recordConfigUseRecord(Long configId, Long userId) {
		MiniPopularizeConfigUseRecord miniPopularizeConfigUseRecord = new MiniPopularizeConfigUseRecord();
		miniPopularizeConfigUseRecord.setConfigId(configId);
		miniPopularizeConfigUseRecord.setUserId(userId);
		miniPopularizeConfigUseRecordMapper.insert(miniPopularizeConfigUseRecord);
	}

	private void shareCountCheck(Long userId) {
		String shareCountKey = RedisService.key.TG_MINI_USER_SAHRE_COUNT_KEY.getName() + DateTime.now().toDateStr() + ":" + userId;
		Object shareCount = redisService.get(shareCountKey);
		if (shareCount != null) {
			int i = Integer.parseInt(shareCount.toString());
			if (i >= 20) {
				throw BusinessRuntimeException.getInstance("已达到最大分享次数");
			}
		} else redisService.setNx(shareCountKey, 0l, RedisService.key.TG_MINI_USER_SAHRE_COUNT_KEY.getTimeout());
		redisService.incr(shareCountKey, 1l);
	}

	private void decrMiniUserGptNum(Long userId) {
		//查询当前用户信息
		MiniPopularizeUser user = miniPopularizeUserMapper.selectOne(Wrappers.lambdaQuery(MiniPopularizeUser.class).eq(MiniPopularizeUser::getUserId, userId));
		if (user.getNum() == 0) {
			throw BusinessRuntimeException.getInstance("无可用次数");
		}
		int i = miniPopularizeUserMapper.decrNum(userId, 1, user.getNum());
		if (i == 0) {
			throw BusinessRuntimeException.getInstance("网络异常，请稍后再试");
		}
	}

	private void newGptModelCreateImage(MiniAiPicturesReq miniAiPicturesReq, MiniPopularizeUserConversationChat imageRecord) throws Exception {
		String tips = miniAiPicturesReq.getTips();
		Integer type = miniAiPicturesReq.getType();
		Request request;
		List<Path> temFilePaths = new ArrayList<>();
		//按提示词出图
		if (type == 1) {
			//出图数量  1-10 默认4张
			JSONObject json = new JSONObject();
			json.putOpt("model", "sora_image");
			json.putOpt("n", 4);
			json.putOpt("size", "1024x1024");
			json.putOpt("response_format", "b64_json");
			json.putOpt("prompt", tips);
			ObjectMapper mapper = new ObjectMapper();
			String requestBody = mapper.writeValueAsString(json);
			request = new Request.Builder()
					.url(NEW_MODEL_GPT_URL)
					.post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()),
							requestBody))
					.header(Header.AUTHORIZATION.name(), GPT_DRAW_TOKEN)
					.build();
		} else {
			String img = miniAiPicturesReq.getImg();
			JSONArray imgs = JSONUtil.parseArray(img);
			// 创建 MultipartBody.Builder 来处理多个文件上传
			MultipartBody.Builder builder = new MultipartBody.Builder()
					.setType(MultipartBody.FORM)
					.addFormDataPart("model", "sora_image")
					.addFormDataPart("n", "4")
					.addFormDataPart("response_format", "b64_json")
					.addFormDataPart("size", "1024x1024")
					.addFormDataPart("prompt", tips);
			//垫图
			imgs.forEach(mg -> {
				byte[] body = HttpUtil.downloadBytes(mg.toString());
				Path tempFile = null;
				try {
					tempFile = Files.createTempFile("mgpt", ".jpeg");
					try (FileImageOutputStream imageOutput = new FileImageOutputStream(tempFile.toFile())) {
						imageOutput.write(body, 0, body.length);
					}
					// 读取图片文件
					builder.addFormDataPart("image[]", tempFile.getFileName().toString(), RequestBody.create(MediaType.parse("image/png"), tempFile.toFile()));
					temFilePaths.add(tempFile);
				} catch (IOException e) {
					log.error("垫图失败：{}", StringUtil.getErrorText(e));
					if (CollUtil.isNotEmpty(temFilePaths)) {
						temFilePaths.forEach(file -> FileUtil.del(file));
					}
				}
			});
			// 创建请求体
			RequestBody requestBody = builder.build();
			// 创建请求对象
			request = new Request.Builder()
					.url(NEW_MODEL_GPT_EDIT_URL)
					.header(Header.AUTHORIZATION.name(), GPT_DRAW_TOKEN)
					.post(requestBody)
					.build();
		}
		OkHttpClient client = new OkHttpClient.Builder()
				// 设置连接超时，单位：秒
				.connectTimeout(300, TimeUnit.SECONDS)
				// 设置读取超时，单位：秒
				.readTimeout(300, TimeUnit.SECONDS)
				.build();
		TASK_EXECUTOR.execute(() -> {
			try {
				Response execute = client.newCall(request).execute();
				ResponseBody body = execute.body();
				JSONObject jsonObject = Jsons.parseObject(body.bytes(), JSONObject.class);
				List<String> imgUrls = new ArrayList<>();
				for (Object data : jsonObject.getJSONArray("data")) {
					JSONObject b64Json = Jsons.parseObject(data, JSONObject.class);
					String base64Image = b64Json.getStr("b64_json");
					//去掉前缀
					base64Image = base64Image.split(",")[1];
					// 解码 Base64 字符串
					byte[] imageBytes = Base64.getDecoder().decode(base64Image);
					String fileName = new StringBuilder().append("mgpt").append(RandomUtil.randomString(9)).append(".png").toString();
					// 保存为图片文件
					try (FileOutputStream fos = new FileOutputStream(fileName)) {
						fos.write(imageBytes);
					} catch (IOException e) {
						e.printStackTrace();
					}
					File file = new File(fileName);
					Map<String, Object> paramMap = new HashMap<>();
					paramMap.put("file", file);
					JSONObject result = JSONUtil.parseObj(HttpUtil.post("https://files.liuliangbang.vip/pic/ups", paramMap));
					String imgUrl = result.getJSONObject("value").getJSONArray("saved").getJSONObject(0).getJSONObject("info").getStr("cdnUrl");
					imgUrls.add(imgUrl);
					if (file.exists()) {
						file.delete();
					}
				}
				imageRecord.setStatus(MiniPopularizeUserConversationChat.Status.success);
				imageRecord.setImg(imgUrls.toString());
				imageRecord.setIsUpload(true);
				conversationChatMapper.updateById(imageRecord);
			} catch (Exception e) {
				log.error("生成小程序GPT图片错误:{}", StringUtil.getErrorText(e));
				//将用户使用次数返回
				miniPopularizeCommonService.recoverUserLimit(imageRecord.getUserId(), true);
				imageRecord.setStatus(MiniPopularizeUserConversationChat.Status.fail);
				imageRecord.setContent(StringUtil.getErrorText(e));
				miniPopularizeCommonService.saveImgRecord(imageRecord);
			} finally {
				if (CollUtil.isNotEmpty(temFilePaths)) {
					temFilePaths.forEach(file -> FileUtil.del(file));
				}
			}
		});
	}
}
