package com.cmvr.edge.client.service.impl;

import cmvr.api.CameraCommand;
import cmvr.api.CameraServiceGrpc;
import cmvr.api.Common;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.cmvr.common.enums.FileType;
import com.cmvr.common.exception.GlobalException;
import com.cmvr.edge.client.file.ByteArrayMultipartFile;
import com.cmvr.edge.client.manage.GrpcServiceManager;
import com.cmvr.edge.client.model.EdgeCommonVO;
import com.cmvr.edge.client.service.EdgeCameraService;
import com.cmvr.edge.client.service.EdgeStreamService;
import com.cmvr.edge.client.utils.EdgeCommonUtil;
import com.cmvr.edge.client.utils.H265DecoderUtil;
import com.cmvr.framework.websocket.service.MessagePushService;
import com.cmvr.system.service.ISysFileService;
import com.google.protobuf.ByteString;
import io.grpc.stub.StreamObserver;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Iterator;
import java.util.List;


@Slf4j
@Service
@RequiredArgsConstructor
public class EdgeCameraServiceImpl implements EdgeCameraService, EdgeStreamService {

    private final GrpcServiceManager grpcServiceManager;
    private final ISysFileService sysFileService;

    private final MessagePushService messagePushService;

    @Override
    public CameraCommand.CameraState status(EdgeCommonVO edgeCommonVO) {
        CameraServiceGrpc.CameraServiceBlockingStub stub = grpcServiceManager.getGrpcClient(edgeCommonVO.getTerminalId(), CameraServiceGrpc.CameraServiceBlockingStub.class);
        CameraCommand.GetCameraStateCommand.Request request = CameraCommand.GetCameraStateCommand.Request.newBuilder()
                .setHeader(EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId()))
                .build();
        return executeGrpcCall(() -> stub.getStatus(request)).getState();
    }

    @Override
    public boolean checkStatus(EdgeCommonVO edgeCommonVO) {
        CameraCommand.CameraState state = status(edgeCommonVO);
        if (state.getIsError()) {
            throw new GlobalException("Camera is in error state");
        }
        return state.getIsOpened() && state.getIsStreaming();
    }

    @Override
    public String start(EdgeCommonVO edgeCommonVO) {
        CameraServiceGrpc.CameraServiceBlockingStub stub = grpcServiceManager.getGrpcClient(edgeCommonVO.getTerminalId(), CameraServiceGrpc.CameraServiceBlockingStub.class);
        CameraCommand.StartCameraCommand.Request request = CameraCommand.StartCameraCommand.Request.newBuilder()
                .setHeader(EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId()))
                .build();
        Common.CommandHeader.Feedback header = executeGrpcCall(() -> stub.startCamera(request)).getHeader();
        return JSON.toJSONString(header);
    }

    @Override
    public String stop(EdgeCommonVO edgeCommonVO) {
        CameraServiceGrpc.CameraServiceBlockingStub stub = grpcServiceManager.getGrpcClient(edgeCommonVO.getTerminalId(), CameraServiceGrpc.CameraServiceBlockingStub.class);
        CameraCommand.StopCameraCommand.Request request = CameraCommand.StopCameraCommand.Request.newBuilder()
                .setHeader(EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId()))
                .build();
        Common.CommandHeader.Feedback header = executeGrpcCall(() -> stub.stopCamera(request)).getHeader();
        return JSON.toJSONString(header);
    }

    @Override
    public String getRGBImage(EdgeCommonVO edgeCommonVO) {

        if (!checkStatus(edgeCommonVO)) {
            start(edgeCommonVO);
        }
        CameraServiceGrpc.CameraServiceBlockingStub stub = grpcServiceManager.getGrpcClient(edgeCommonVO.getTerminalId(), CameraServiceGrpc.CameraServiceBlockingStub.class);
        CameraCommand.GetRGBImageCommand.Request request = CameraCommand.GetRGBImageCommand.Request.newBuilder()
                .setHeader(EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId()))
                .build();
        CameraCommand.GetRGBImageCommand.Feedback feedback = executeGrpcCall(() -> stub.getRGBImage(request));
        CameraCommand.FrameData frame = feedback.getColorFrame();
        if (!feedback.hasColorFrame() || frame.getWidth() <= 0 || frame.getHeight() <= 0) {
            throw new GlobalException("响应图片数据无效");
        }
        String filePath = saveImage(frame.getData(), frame.getWidth(), frame.getHeight());
        try {
            MultipartFile file = convertFileToMultipartFile(filePath);
            return sysFileService.uploadFile(file, FileType.IMAGE.code());
        } catch (IOException e) {
            throw new GlobalException("图片上传失败: " + e.getMessage());
        } finally {
            deleteTempFile(filePath);
//             todo 暂时不关闭摄像头
//            stop(terminalId, deviceId);
        }
    }

    @Override
    public String startRecording(EdgeCommonVO edgeCommonVO) {
        if (!checkStatus(edgeCommonVO)) {
            start(edgeCommonVO);
        }
        CameraServiceGrpc.CameraServiceBlockingStub stub = grpcServiceManager.getGrpcClient(edgeCommonVO.getTerminalId(), CameraServiceGrpc.CameraServiceBlockingStub.class);
        String videoPath = StrUtil.format("{}/{}_{}.mp4", "/home/share/assets/video", edgeCommonVO.getDeviceId(), System.currentTimeMillis());

        CameraCommand.StartCameraRecordingCommand.Request request = CameraCommand.StartCameraRecordingCommand.Request.newBuilder()
                .setHeader(EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId()))
                .setVideoPath(videoPath)
                .build();
        executeGrpcCall(() -> stub.startRecording(request));
        return videoPath;
    }

    @Override
    public String stopRecording(EdgeCommonVO edgeCommonVO) {
        CameraServiceGrpc.CameraServiceBlockingStub stub = grpcServiceManager.getGrpcClient(edgeCommonVO.getTerminalId(), CameraServiceGrpc.CameraServiceBlockingStub.class);
        CameraCommand.StopCameraRecordingCommand.Request request = CameraCommand.StopCameraRecordingCommand.Request.newBuilder()
                .setHeader(EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId()))
                .build();
        Common.CommandHeader.Feedback header = executeGrpcCall(() -> stub.stopRecording(request)).getHeader();
        return JSON.toJSONString(header);
    }

    @Override
    public String getRGBDImages(EdgeCommonVO edgeCommonVO) {
        if (!checkStatus(edgeCommonVO)) {
            start(edgeCommonVO);
        }

        CameraServiceGrpc.CameraServiceBlockingStub stub = grpcServiceManager.getGrpcClient(edgeCommonVO.getTerminalId(), CameraServiceGrpc.CameraServiceBlockingStub.class);
        CameraCommand.GetRGBDImagesCommand.Request request = CameraCommand.GetRGBDImagesCommand.Request.newBuilder()
                .setHeader(EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId()))
                .build();
        CameraCommand.GetRGBDImagesCommand.Feedback feedback = executeGrpcCall(() -> stub.getRGBDImages(request));

        // 彩色图和深度图
        CameraCommand.FrameData frame = feedback.getColorFrame();
//        CameraCommand.FrameData depthFrame = feedback.getDepthFrame();
        if (!feedback.hasColorFrame() || frame.getWidth() <= 0 || frame.getHeight() <= 0) {
            throw new GlobalException("响应图片数据无效");
        }
        String filePath = saveImage(frame.getData(), frame.getWidth(), frame.getHeight(), false);
        log.info("彩色图片地址：{}", filePath);
//        String deepFilePath = saveImage(depthFrame.getData(), depthFrame.getWidth(), depthFrame.getHeight(), true);
//        List<String> result = new ArrayList<>();
        try {
            MultipartFile file = convertFileToMultipartFile(filePath);
//            MultipartFile deepFile = convertFileToMultipartFile(deepFilePath);
            return sysFileService.uploadFile(file, FileType.IMAGE.code());
//            result.add(imageUrl);
//            String deepImageUrl = sysFileService.uploadFile(deepFile, FileType.IMAGE.code());
//            result.add(deepImageUrl);
        } catch (IOException e) {
            throw new GlobalException("图片上传失败: " + e.getMessage());
        } finally {
//            deleteTempFile(filePath);
//            deleteTempFile(deepFilePath);
//             todo 暂时不关闭摄像头
//            stop(terminalId, deviceId);
        }
//        return JSON.toJSONString(result);
    }

    // 缓冲区和收集状态
    private final List<byte[]> framesBuffer = new ArrayList<>();
    private boolean isCollectingFrames = false;

    @Override
    public StreamObserver<CameraCommand.GetRGBImageStreamCommand.Request> getRGBImageStream(EdgeCommonVO edgeCommonVO) {
        if (!checkStatus(edgeCommonVO)) {
            start(edgeCommonVO);
        }
        String terminalId = edgeCommonVO.getTerminalId();
        String deviceId = edgeCommonVO.getDeviceId();
        CameraServiceGrpc.CameraServiceStub stub = grpcServiceManager.getGrpcClient(terminalId, CameraServiceGrpc.CameraServiceStub.class);
        // 创建StreamObserver来处理响应
        StreamObserver<CameraCommand.GetRGBImageStreamCommand.Feedback> responseObserver = new StreamObserver<CameraCommand.GetRGBImageStreamCommand.Feedback>() {
            @Override
            public void onNext(CameraCommand.GetRGBImageStreamCommand.Feedback value) {
                CameraCommand.FrameData frame = value.getColorFrame();
                byte[] frameData = frame.getData().toByteArray();

                if (frame.getIsKeyFrame()) { // I帧
                    if (isCollectingFrames) {
                        // 处理之前的缓冲区数据
                        String videoBase64 = H265DecoderUtil.convertH265FramesToVideo(framesBuffer);
                        if (videoBase64 != null) {
                            messagePushService.pushToChannel(
                                    "edgeCameraServiceImpl/getRGBImageStream/" + terminalId + "/" + deviceId,
                                    videoBase64
                            );
                        }
                        // 重置缓冲区
                        framesBuffer.clear();
                    }
                    // 开始新收集段
                    framesBuffer.add(frameData);
                    isCollectingFrames = true;
                } else {
                    // 非I帧且正在收集时加入缓冲区
                    if (isCollectingFrames) {
                        framesBuffer.add(frameData);
                    }
                }
            }

            @Override
            public void onError(Throwable t) {
                // 记录详细的错误信息
                log.error("Error occurred in getRGBImageStream for terminalId: {}, deviceId: {}. Error: {}", terminalId, deviceId, t.getMessage(), t);
                // 推送错误信息
                messagePushService.pushToChannel("edgeCameraServiceImpl/getRGBImageStream/" + terminalId + "/" + deviceId, "500");
            }

            @Override
            public void onCompleted() {
                // 流结束
                System.out.println("Stream completed.");
            }
        };
        // 创建StreamObserver来发送请求
        StreamObserver<CameraCommand.GetRGBImageStreamCommand.Request> requestObserver = stub.getRGBImageStream(responseObserver);

        // 构建请求并发送
        CameraCommand.GetRGBImageStreamCommand.Request request = CameraCommand.GetRGBImageStreamCommand.Request.newBuilder()
                .setHeader(EdgeCommonUtil.buildRequest(deviceId))
                .setEof(false)
                .build();

        requestObserver.onNext(request);
        return requestObserver;

    }

    @Override
    public void getDepthImageStream(EdgeCommonVO edgeCommonVO) {

    }

    @Override
    public void getRGBDImagesStream(EdgeCommonVO edgeCommonVO) {

    }

    @FunctionalInterface
    private interface GrpcCall<T> {
        T call() throws Exception;
    }

    private <T> T executeGrpcCall(GrpcCall<T> call) {
        try {
            return call.call();
        } catch (Exception e) {
            throw new GlobalException("gRPC 调用失败: " + e.getMessage());
        }
    }

    private String rgbToBase64Bmp(byte[] rgbData, int width, int height) throws IOException {
        int rowSize = (width * 3 + 3) & ~3; // BMP 行对齐
        int imageSize = rowSize * height;
        int fileSize = 54 + imageSize;

        ByteArrayOutputStream baos = new ByteArrayOutputStream(fileSize);
        DataOutputStream dos = new DataOutputStream(baos);

        // BMP Header
        dos.writeBytes("BM");
        dos.writeInt(Integer.reverseBytes(fileSize));
        dos.writeInt(0);
        dos.writeInt(Integer.reverseBytes(54));

        // DIB Header
        dos.writeInt(Integer.reverseBytes(40));
        dos.writeInt(Integer.reverseBytes(width));
        dos.writeInt(Integer.reverseBytes(height));
        dos.writeShort(Short.reverseBytes((short) 1));
        dos.writeShort(Short.reverseBytes((short) 24));
        dos.writeInt(0);
        dos.writeInt(Integer.reverseBytes(imageSize));
        dos.writeInt(0);
        dos.writeInt(0);
        dos.writeInt(0);
        dos.writeInt(0);

        // Pixel Data (BMP 是 BGR 顺序，从下往上存储)
        for (int y = height - 1; y >= 0; y--) {
            int rowStart = y * width * 3;
            for (int x = 0; x < width; x++) {
                int pixelStart = rowStart + x * 3;
                dos.write(rgbData[pixelStart + 0]); // B
                dos.write(rgbData[pixelStart + 1]); // G
                dos.write(rgbData[pixelStart + 2]);     // R
            }
            // 行填充
            for (int i = width * 3; i < rowSize; i++) {
                dos.write(0);
            }
        }

        byte[] bmpBytes = baos.toByteArray();
        return Base64.getEncoder().encodeToString(bmpBytes);
    }

    /**
     * 临时保存图片
     */
    private String saveImage(ByteString pixelData, int width, int height) {
        File tempDir = new File(System.getProperty("java.io.tmpdir"));
        FileUtil.mkdir(tempDir);

        // 生成唯一文件名（时间戳）
        String fileName = System.currentTimeMillis() + ".jpg";
        String filePath = Paths.get(tempDir.getAbsolutePath(), fileName).toString();
        try {
            byte[] pixels = pixelData.toByteArray();

            // 每个像素 3 字节（R, G, B），按行排列
            DataBuffer buffer = new DataBufferByte(pixels, pixels.length);

            // 每像素 3 字节，每行 stride 是 3 * width
            WritableRaster raster = Raster.createInterleavedRaster(
                    buffer, width, height, 3 * width, 3,
                    new int[]{2, 1, 0}, null
            );

            ColorModel colorModel = new ComponentColorModel(
                    ColorSpace.getInstance(ColorSpace.CS_sRGB),
                    false, false,
                    Transparency.OPAQUE,
                    DataBuffer.TYPE_BYTE
            );

            BufferedImage image = new BufferedImage(colorModel, raster, false, null);
            File output = new File(filePath);
            ImageIO.write(image, "jpg", output);
        } catch (IOException e) {
            throw new GlobalException(e.getMessage());
        }
        return filePath;
    }

    private String saveImage(ByteString pixelData, int width, int height, boolean isDepth) {
        File tempDir = new File(System.getProperty("java.io.tmpdir"));
        FileUtil.mkdir(tempDir);

        String fileName = System.currentTimeMillis() + (isDepth ? "_depth" : "") + ".jpg";
        String filePath = Paths.get(tempDir.getAbsolutePath(), fileName).toString();

        try {
            byte[] pixels = pixelData.toByteArray();
            BufferedImage image;

            if (isDepth) {
                // 灰度深度图
                DataBuffer buffer = new DataBufferByte(pixels, pixels.length);
                WritableRaster raster = Raster.createInterleavedRaster(buffer, width, height, width, 1, new int[]{0}, null);
                ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false,
                        Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
                image = new BufferedImage(cm, raster, false, null);
            } else {
                // 彩色RGB
                DataBuffer buffer = new DataBufferByte(pixels, pixels.length);
                WritableRaster raster = Raster.createInterleavedRaster(buffer, width, height, 3 * width, 3,
                        new int[]{1, 2, 0}, null);
                ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), false, false,
                        Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
                image = new BufferedImage(cm, raster, false, null);
            }

            // 设置高质量JPEG压缩
            Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");
            ImageWriter writer = writers.next();

            try (FileImageOutputStream output = new FileImageOutputStream(new File(filePath))) {
                writer.setOutput(output);
                ImageWriteParam param = writer.getDefaultWriteParam();
                if (param.canWriteCompressed()) {
                    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                    param.setCompressionQuality(0.95f); // 压缩质量/压缩比 95%
                }
                writer.write(null, new IIOImage(image, null, null), param);
            }
            writer.dispose();

        } catch (IOException e) {
            throw new GlobalException(e.getMessage());
        }
        return filePath;
    }


    /**
     * 将本地文件转换为 MultipartFile
     */
    private MultipartFile convertFileToMultipartFile(String filePath) throws IOException {
        File file = new File(filePath);
        byte[] content = Files.readAllBytes(file.toPath());

        return new ByteArrayMultipartFile(
                file.getName(),
                file.getName(),
                MediaType.IMAGE_JPEG_VALUE,
                content
        );
    }

    /**
     * 删除临时文件
     */
    private void deleteTempFile(String filePath) {
        if (StrUtil.isNotEmpty(filePath)) {
            File file = new File(filePath);
            if (FileUtil.exist(file) && !FileUtil.del(file)) {
                log.warn("临时文件删除失败, 路径：{}", filePath);
            }
        }
    }
}
