package com.inspect.ivs.service;

import com.alibaba.fastjson.JSONObject;
import com.inspect.base.core.domain.Response;
import com.inspect.base.core.utils.StringUtils;
import com.inspect.ivs.base.feign.domain.IvsResult;
import com.inspect.ivs.constant.IvsConst;
import com.inspect.ivs.util.UriUtils;
import com.inspect.ivs.view.IvsPlatformSnapshotView;
import com.inspect.ivs.view.IvsSnapshotView;
import com.inspect.ivs.vo.*;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.ResponseEntity;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.retry.support.RetrySynchronizationManager;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

@Component
public class IvsResourceRetryableDelegate {
    private static final Logger log = LoggerFactory.getLogger(IvsResourceRetryableDelegate.class);

    private static final int RETRYABLE_MIN = 5;

    private static final int RETRYABLE_MAX = 20;

    @Value("${task.test-mode:false}")
    private boolean testMode;

    @Resource
    private IvsCommonService ivsCommonService;

    // 转到预置位
    @Retryable(
            value = RuntimeException.class,
            maxAttempts = RETRYABLE_MAX,
            backoff = @Backoff(delay = 2000)) // 每次重试间隔 2 秒
    public Response<Void> gotoSnapshotPresetRetryable(String version, IvsPresetVo ivsPresetVo) {
        int retryCount = RetrySynchronizationManager.getContext() != null
                ? RetrySynchronizationManager.getContext().getRetryCount() + 1
                : 1;
        log.info("URI_PTZ_CONTROL GOTO: retryCount: {}, BODY: {}",
                retryCount,
                ivsPresetVo);
        JSONObject paramJson = new JSONObject();
        paramJson.put("cameraCode", ivsPresetVo.getCameraCode());
        log.debug("URI_PTZ_CONTROL GOTO: IVS VERSION: {}", version);
        if (version.equals("1800")) {
            paramJson.put("controlCode", 11);
        } else {
            paramJson.put("controlCode", "11");
        }
        paramJson.put("controlPara1", ivsPresetVo.getPreset());
        paramJson.put("controlPara2", "");
        if (StringUtils.isNotEmpty(ivsPresetVo.getHost())
                && !"null".equals(ivsPresetVo.getHost())) {
            paramJson.put("host", ivsPresetVo.getHost());
            paramJson.put("port", ivsPresetVo.getPort());
            paramJson.put("address", ivsPresetVo.getAddress());
            paramJson.put("username", ivsPresetVo.getUsername());
            paramJson.put("password", ivsPresetVo.getPassword());
        }
        try {
            final String apiAddr = getApiAddress(ivsPresetVo);
            log.info("URI_PTZ_CONTROL GOTO apiAddr: {}", apiAddr);
            ivsCommonService.postJson(ivsPresetVo.getUUID(), apiAddr, IvsConst.URI_PTZ_CONTROL, paramJson, IvsResult.class);
            log.info("URI_PTZ_CONTROL SUCCESS retryCount: {}, apiAddr: {}", retryCount, apiAddr);
        } catch (Exception e) {
            log.info("URI_PTZ_CONTROL GOTO FAIL retryCount: {}, UUID: {}", retryCount, ivsPresetVo.getUUID());
            //throw new IOException("URI_PTZ_CONTROL GOTO: FAIL UUID: " + ivsPresetVo.getUUID() + ", retryCount: " + retryCount + ", MSG: " + e);
            throw new RuntimeException("URI_PTZ_CONTROL GOTO: FAIL UUID: " + ivsPresetVo.getUUID() + ", retryCount: " + retryCount + ", MSG: " + e);
        }
        return Response.ok();
    }

    @SuppressWarnings("unused")
    @Recover
    public Response<Void> recover(RuntimeException e, String version, IvsPresetVo ivsPresetVo) {
        log.info("URI_PTZ_CONTROL GOTO RECOVER UUID: {}, MSG: {}", ivsPresetVo.getUUID(), e.getMessage());
        return Response.fail();
    }

    // 转到预置位
    @Retryable(
            value = RuntimeException.class,
            maxAttempts = RETRYABLE_MAX,
            backoff = @Backoff(delay = 2000)) // 每次重试间隔 2 秒
    public Response<Void> gotoSnapshotPresetExRetryable(String version, IvsPresetExVo ivsPresetVo) {
        int retryCount = RetrySynchronizationManager.getContext() != null
                ? RetrySynchronizationManager.getContext().getRetryCount() + 1
                : 1;
        log.info("URI_PTZ_CONTROL_EX CORRECTION: retryCount: {}, BODY: {}",
                retryCount,
                ivsPresetVo);
        log.debug("URI_PTZ_CONTROL_EX CORRECTION: IVS VERSION: {}", version);
        JSONObject paramJson = new JSONObject();
        paramJson.put("cameraCode", ivsPresetVo.getCode());
        paramJson.put("controlCode", 31);
        paramJson.put("controlPara1", ivsPresetVo.getControlPara1());
        paramJson.put("controlPara2", ivsPresetVo.getControlPara2());
        try {
            final String apiAddr = getApiAddress(ivsPresetVo);
            log.info("URI_PTZ_CONTROL_EX CORRECTION apiAddr: {}", apiAddr);
            ivsCommonService.postJson(ivsPresetVo.getUUID(), apiAddr, IvsConst.URI_PTZ_CONTROL, paramJson, IvsResult.class);
            log.info("URI_PTZ_CONTROL_EX CORRECTION SUCCESS retryCount: {}, apiAddr: {}", retryCount, apiAddr);
        } catch (Exception e) {
            log.info("URI_PTZ_CONTROL_EX CORRECTION FAIL retryCount: {}, UUID: {}", retryCount, ivsPresetVo.getUUID());
            //throw new IOException("URI_PTZ_CONTROL GOTO: FAIL UUID: " + ivsPresetVo.getUUID() + ", retryCount: " + retryCount + ", MSG: " + e);
            throw new RuntimeException("URI_PTZ_CONTROL_EX CORRECTION: FAIL UUID: " + ivsPresetVo.getUUID() + ", retryCount: " + retryCount + ", MSG: " + e);
        }
        return Response.ok();
    }

    @SuppressWarnings("unused")
    @Recover
    public Response<Void> recover(RuntimeException e, String version, IvsPresetExVo ivsPresetVo) {
        log.info("URI_PTZ_CONTROL_EX CORRECTION RECOVER UUID: {}, MSG: {}", ivsPresetVo.getUUID(), e.getMessage());
        return Response.fail();
    }

    // 下发拍照命令
    @Retryable(
            value = IOException.class,
            maxAttempts = RETRYABLE_MAX,
            backoff = @Backoff(delay = 2000)) // 每次重试间隔 2 秒
    public IvsSnapshotView getSnapshotRetryable(IvsDevChanSnapVo ivsDevChanSnapVo) throws IOException {
        int retryCount = RetrySynchronizationManager.getContext() != null
                ? RetrySynchronizationManager.getContext().getRetryCount() + 1
                : 1;
        log.info("URI_PLATFORM_SNAPSHOT retryCount: {}, BODY: {}",
                retryCount,
                ivsDevChanSnapVo);
        IvsSnapshotView ivsSnapshotView;
        try {
            Set<String> ignoredKeys = new HashSet<>();
            ignoredKeys.add("uUID");
            ignoredKeys.add("name");
            ignoredKeys.add("id");
            if (StringUtils.isEmpty(ivsDevChanSnapVo.getHost())
                    && !"null".equals(ivsDevChanSnapVo.getHost())) {
                ignoredKeys.add("host");
                ignoredKeys.add("port");
                ignoredKeys.add("address");
                ignoredKeys.add("username");
                ignoredKeys.add("password");
            }
            final String apiAddr = getApiAddress(ivsDevChanSnapVo);
            log.info("URI_PLATFORM_SNAPSHOT apiAddr: {}", apiAddr);
            final String uri = UriUtils.parseIgnored(IvsConst.URI_PLATFORM_SNAPSHOT, ivsDevChanSnapVo, ignoredKeys);
            //ivsSnapshotView = ivsCommonService.get(ivsDevChanSnapVo.getUUID(), UriUtils.parse(IvsConst.URI_PLATFORM_SNAPSHOT, ivsDevChanSnapVo), IvsSnapshotView.class);
            log.info("URI_PLATFORM_SNAPSHOT retryCount: {}, UUID: {}, URI: {}", retryCount, ivsDevChanSnapVo.getUUID(), uri);
            ivsSnapshotView = ivsCommonService.get(ivsDevChanSnapVo.getUUID(), apiAddr, uri, IvsSnapshotView.class);
            log.info("URI_PLATFORM_SNAPSHOT SUCCESS retryCount: {}, UUID: {}", retryCount, ivsDevChanSnapVo.getUUID());
        } catch (Exception e) {
            log.info("URI_PLATFORM_SNAPSHOT FAIL retryCount: {}, UUID: {}", retryCount, ivsDevChanSnapVo.getUUID());
            throw new IOException("URI_PLATFORM_SNAPSHOT FAIL UUID: " + ivsDevChanSnapVo.getUUID() + ", retryCount: " + retryCount);
        }

        return ivsSnapshotView;
    }

    @SuppressWarnings("unused")
    @Recover
    public IvsSnapshotView recover(IOException e, IvsDevChanSnapVo ivsDevChanSnapVo) {
        log.info("URI_PLATFORM_SNAPSHOT RECOVER UUID: {}, MSG: {}", ivsDevChanSnapVo.getUUID(), e.getMessage());
        return null;
    }

    // 获取照片信息(下载路径)
    @Retryable(
            value = IOException.class,
            maxAttempts = RETRYABLE_MIN,
            backoff = @Backoff(delay = 2000)) // 每次重试间隔 2 秒
    public IvsPlatformSnapshotView getSnapshotListRetryable(final IvsChanSnapVo ivsChanSnapVo) throws IOException {
        int retryCount = RetrySynchronizationManager.getContext() != null
                ? RetrySynchronizationManager.getContext().getRetryCount() + 1
                : 1;
        log.info("URI_SNAPSHOT_LIST retryCount: {}, BODY: {}",
                retryCount,
                ivsChanSnapVo);
        final String uuid = ivsChanSnapVo.getUUID();
        IvsPlatformSnapshotView snapShotView;
        try {
            final String apiAddr = getApiAddress(ivsChanSnapVo);
            ivsChanSnapVo.setUUID(null);
            ivsChanSnapVo.setName(null);
            ivsChanSnapVo.setId(null);
            if (StringUtils.isEmpty(ivsChanSnapVo.getHost())
                    && !"null".equals(ivsChanSnapVo.getHost())) {
                ivsChanSnapVo.setHost(null);
                ivsChanSnapVo.setPort(null);
                ivsChanSnapVo.setAddress(null);
                ivsChanSnapVo.setUsername(null);
                ivsChanSnapVo.setPassword(null);
            }
            log.info("URI_SNAPSHOT_LIST apiAddr: {}", apiAddr);
            snapShotView = ivsCommonService.postJson(
                    ivsChanSnapVo.getUUID(),
                    apiAddr,
                    IvsConst.URI_SNAPSHOT_LIST,
                    ivsChanSnapVo,
                    IvsPlatformSnapshotView.class);
            log.info("URI_SNAPSHOT_LIST SUCCESS retryCount: {}, UUID: {}, snapShotView: {}", retryCount, uuid, snapShotView);
        } catch (Exception e) {
            log.info("URI_SNAPSHOT_LIST FAIL retryCount: {}, UUID: {}", retryCount, uuid);
            throw new IOException("URI_SNAPSHOT_LIST FAIL UUID: " + uuid);
        }

        return snapShotView;
    }

    @SuppressWarnings("unused")
    @Recover
    public IvsPlatformSnapshotView recover(IOException e, IvsChanSnapVo ivsChanSnapVo) {
        log.info("URI_SNAPSHOT_LIST RECOVER UUID: {}, MSG: {}", ivsChanSnapVo.getUUID(), e.getMessage());
        return null;
    }

    // 下载照片
    @Retryable(
            value = IOException.class,
            maxAttempts = RETRYABLE_MIN,
            backoff = @Backoff(delay = 2000)) // 每次重试间隔 2 秒
    public ResponseEntity<InputStreamResource> downloadPictureRetryable(String uuid, String pictureUrl) throws IOException {
        int retryCount = RetrySynchronizationManager.getContext() != null
                ? RetrySynchronizationManager.getContext().getRetryCount() + 1
                : 1;

        log.info("DOWNLOAD_PICTURE retryCount: {}, pictureUrl: {}, UUID: {}", retryCount, pictureUrl, uuid);
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpResponse httpResponse = httpClient.execute(new HttpGet(pictureUrl));
            InputStream content = httpResponse.getEntity().getContent();
            byte[] bytes = readStream(content);
            log.info("DOWNLOAD_PICTURE retryCount: {}, SIZE: {}, UUID: {}", retryCount, bytes.length, uuid);
            if (bytes.length > 0) {
                InputStream inputStream = new ByteArrayInputStream(bytes);
                return ResponseEntity.ok().body(new InputStreamResource(inputStream));
            } else {
                throw new IOException("DOWNLOAD_PICTURE PIC NULL: " + pictureUrl + ", UUID: " + uuid);
            }
        }
    }

    @SuppressWarnings("unused")
    @Recover
    public ResponseEntity<InputStreamResource> recover(IOException e, String uuid, String pictureUrl) {
        log.info("DOWNLOAD_PICTURE RECOVER {}, UUID: {}, MSG: {}", pictureUrl, uuid, e.getMessage());
        return ResponseEntity.ok().body(new InputStreamResource(generateErrorImage()));
    }

    public static byte[] readStream(InputStream inputStream) throws IOException {
        try (ByteArrayOutputStream outStream = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[4096]; // 4KB 缓冲区
            int len;
            while ((len = inputStream.read(buffer)) != -1) {
                outStream.write(buffer, 0, len);
            }
            return outStream.toByteArray();
        }
    }

    public InputStream generateErrorImage() {
        try {
            byte[] bytes;
            if (testMode) {
                bytes = loadDefaultImage();
            } else {
                bytes = loadErrorImage();
            }
            return new ByteArrayInputStream(bytes);
        } catch (IOException e) {
            throw new RuntimeException("生成错误图片失败", e);
        }
    }

//    public InputStream generateErrorImage() {
//        int width = 400, height = 200;
//        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//        Graphics2D g = bufferedImage.createGraphics();
//
//        // 背景
//        g.setColor(Color.WHITE);
//        g.fillRect(0, 0, width, height);
//
//        // 字体
//        g.setColor(Color.RED);
//        g.setFont(new Font("Arial", Font.BOLD, 20));
//        g.drawString("Get Picture Fail", 120, 100);
//        g.dispose();
//
//        try {
//            ByteArrayOutputStream os = new ByteArrayOutputStream();
//            ImageIO.write(bufferedImage, "png", os);
//            byte[] bytes;
//            if (testMode) {
//                bytes = loadDefaultImage();
//            } else {
//                bytes = os.toByteArray();
//            }
//            return new ByteArrayInputStream(bytes);
//        } catch (IOException e) {
//            throw new RuntimeException("生成错误图片失败", e);
//        }
//    }

    public byte[] loadErrorImage() throws IOException {
        ClassPathResource imgFile = new ClassPathResource("images/ivs_error.jpg");
        return StreamUtils.copyToByteArray(imgFile.getInputStream());
    }

    public byte[] loadDefaultImage() throws IOException {
        ClassPathResource imgFile = new ClassPathResource("images/ivs_default.jpg");
        return StreamUtils.copyToByteArray(imgFile.getInputStream());
    }

    public String getLabel() {
        return UUID.randomUUID().toString().trim().replaceAll(StringUtils.DASH, StringUtils.EMPTY);
    }

    public String getApiAddress(IvsVo ivsVo) {
        String realAddr = null;
        if (StringUtils.isNotEmpty(ivsVo.getHost())
                && !"null".equals(ivsVo.getHost())) { // nvr ip
            //realAddr = ivsVo.getHost().concat(StringUtils.COLON).concat(ivsVo.getPort());
            realAddr = "http://" + ivsVo.getHost() + StringUtils.COLON + ivsVo.getPort();
        }

        return realAddr;
    }
}
