package com.icomsys.main_vm.biz.rcp.monitoring.service.impl;

import com.icomsys.main_vm.biz.common.common.service.ExcelService;
import com.icomsys.main_vm.biz.common.login.service.LoginService;
import com.icomsys.main_vm.biz.rcp.monitoring.service.MonitoringConsultingService;
import com.icomsys.main_vm.common.code.enumresource.PolicyResource;
import com.icomsys.main_vm.db.mybatis.alias.MonitoringConsultingVO;
import com.icomsys.main_vm.db.mybatis.mapper.MonitoringConsultingMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider;
import software.amazon.awssdk.core.ResponseBytes;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Service
@RequiredArgsConstructor
public class MonitoringConsultingServiceImpl implements MonitoringConsultingService {

    private final HttpServletResponse httpServletResponse;

    private final LoginService loginService;

    @Value("${file.path}")
    private String filePath;

    @Value("${feign.url.s3Bucket}")
    private String s3Bucket;

    @Autowired
    private MonitoringConsultingMapper monitoringConsultingMapper;

    @Override
    public List<HashMap> consultingList(MonitoringConsultingVO vo) {
        if (vo.getTelNo() != null && !"".equals(vo.getTelNo())) {
            String telNo = "";
            telNo = vo.getTelNo().replace("-", "");
            vo.setTelNo(telNo.trim());
        }

        List<HashMap> result = monitoringConsultingMapper.selectConsultingList(vo);

        String telNo = "";

        for (HashMap map : result) {
            if (telNo.length() == 10) {
                if (loginService.getSessionPolicy().contains(PolicyResource.privacymasking.getName())) {
                    telNo = String.valueOf(map.get("telNo"));
                    telNo = telNo.substring(0, 3) + "-" + telNo.substring(3, 6) + "-" + telNo.substring(6, telNo.length());
                    map.put("originTelNo", telNo);
                }

                telNo = String.valueOf(map.get("telNo"));
                //                telNo = telNo.substring(0, 3) + "-***-" + telNo.substring(6, telNo.length());
                telNo = telNo.substring(0, 3) + "-**" + telNo.substring(5, 6) + "-**" + telNo.substring(8, telNo.length());
                map.put("telNo", telNo);
            }
            else {
                if (loginService.getSessionPolicy().contains(PolicyResource.privacymasking.getName())) {
                    telNo = String.valueOf(map.get("telNo"));
                    telNo = telNo.substring(0, 3) + "-" + telNo.substring(3, 7) + "-" + telNo.substring(7, telNo.length());
                    map.put("originTelNo", telNo);
                }

                telNo = String.valueOf(map.get("telNo"));
                //                telNo = telNo.substring(0, 3) + "-****-" + telNo.substring(7, telNo.length());
                telNo = telNo.substring(0, 3) + "-**" + telNo.substring(5, 7) + "-**" + telNo.substring(9, telNo.length());
                map.put("telNo", telNo);
            }
        }

        return result;
    }

    @Override
    public int selectConsultingListCnt(MonitoringConsultingVO vo) {
        return monitoringConsultingMapper.selectConsultingListCnt(vo);
    }

    @Override
    public List<HashMap> selectRecordCallList(MonitoringConsultingVO vo) {
        List<HashMap> hList = monitoringConsultingMapper.selectRecordCallList(vo);
        this.checkRecordFileFromS3(hList);
        return hList;
    }

    @Override
    public List<HashMap> selectScenarioSeqAndName(MonitoringConsultingVO vo) {
        return monitoringConsultingMapper.selectScenarioSeqAndName(vo);
    }

    @Override
    public List<HashMap> selectCommonCodeAndDesc(MonitoringConsultingVO vo) {
        return monitoringConsultingMapper.selectCommonCodeAndDesc(vo);
    }

    @Override
    public int selectUserCallCnt(MonitoringConsultingVO vo) {
        return monitoringConsultingMapper.selectUserCallCnt(vo);
    }

    private void checkRecordFileFromS3(List<HashMap> list) {
        if ("local".equals(s3Bucket)) {
            for (HashMap fMap : list) { // 대화 내용 리스트
                if (fMap.containsKey("recFilePath")) {
                    fMap.put("recFilePath", "");
                }
            }
            return;
        }

        String bucketName = s3Bucket; // 버킷명

        // 1. s3 client 생성
        S3Client s3 = S3Client.builder()
                              .region(Region.AP_NORTHEAST_2)
                              .credentialsProvider(InstanceProfileCredentialsProvider.create())
                              .build();
        log.debug("s3Client create");

        // 2. 버킷 내에 각 파일 확인
        for (HashMap fMap : list) { // 대화 내용 리스트
            if (fMap.containsKey("recFilePath")) {
                String recFilePath = fMap.get("recFilePath").toString();

                boolean isS3Object = false;
                if (!"".equals(recFilePath)) {
                    recFilePath = recFilePath.substring(1);
                    List<S3Object> sList = listBucketObjects(s3,bucketName,recFilePath); // 버킷에 파일 찾기

                    for (S3Object myValue : sList) {
                        if (myValue.key().equals(recFilePath)) {
                            isS3Object = true;
                            log.info("name of the key " + myValue.key() + " exist");
                        }
                    }

                    if (!isS3Object) {
                        recFilePath = "";
                    }
                }
                fMap.put("recFilePath",recFilePath);
            }
        }
    }

    @Override
    public String playRecordFileFromS3(String path) {
        String bucketName = s3Bucket; // 버킷명
        path = path.replaceAll("%2F","/");

        // 1. s3 client 생성
        S3Client s3 = S3Client.builder()
                              .region(Region.AP_NORTHEAST_2)
                              .credentialsProvider(InstanceProfileCredentialsProvider.create())
                              .build();

        // 2. 다운로드 된 파일인지 확인
        String fullPath = filePath + "recordfile/" + recordDownloadFileName(path);

        File file = new File(fullPath);
        if (file.exists()) {
            if (!file.isDirectory()) {
                return recordServerFilePath(recordDownloadFileName(path));
            }
        }

        // 3. 다운로드 되지 않은 경우 다운로드 진행
        return recordServerFilePath(downloadFileFromS3(path, s3, bucketName));

    }

    private List<S3Object> listBucketObjects(S3Client s3, String bucketName, String path) {

        ListObjectsRequest listObjects = ListObjectsRequest
                .builder()
                .prefix(path)
                .bucket(bucketName)
                .build();

        ListObjectsResponse res = s3.listObjects(listObjects);

        return res.contents();
    }

    private String downloadFileFromS3(String keyName, S3Client s3, String bucketName) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        Calendar c1 = Calendar.getInstance();

        String fileFormat = filePath + "recordfile/";
        String fileName = recordDownloadFileName(keyName);
        try {

            log.info("Downloading " + keyName + " from " + bucketName + "S3 bucket ...");

            GetObjectRequest objectRequest = GetObjectRequest
                    .builder()
                    .key(keyName)
                    .bucket(bucketName)
                    .build();

            ResponseBytes<GetObjectResponse> objectBytes = s3.getObjectAsBytes(objectRequest);
            byte[] data = objectBytes.asByteArray();

            File Folder = new File(fileFormat + sdf.format(c1.getTime()));
            if (!Folder.exists()) {
                try {
                    Folder.mkdirs();
                } catch (Exception e) {
                    log.debug("error_mkdir");
                    e.getStackTrace();
                }
            }

            File myFile = new File(fileFormat + fileName);
            OutputStream os = new FileOutputStream(myFile);

            os.write(data);
            log.debug("Successfully obtained " + keyName + " from an S3 object");
            os.close();

        } catch (IOException ex) {
            fileName = "";
            log.error(ex.getMessage());
        } catch (S3Exception e) {
            fileName = "";
            log.error(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        return fileName;
    }

    private String recordDownloadFileName(String keyName) {

        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        Calendar c1 = Calendar.getInstance();
        String fileName = sdf.format(c1.getTime()) + "/";

        String filePattern = "recordfile/[0-9]{8}/";
        Pattern pattern = Pattern.compile(filePattern);
        Matcher match = pattern.matcher(keyName);
        log.debug("pattern check" + keyName);

        if (match.find()) {
            log.debug("record file Name :" + keyName.replaceAll(filePattern,"").trim());
            fileName = fileName + keyName.replaceAll(filePattern,"").trim();
        }

        return fileName;
    }

    private String recordServerFilePath(String keyName) {

        InetAddress local;
        String ip = "";
        try {
            local = InetAddress.getLocalHost();
            ip = local.getHostAddress();
        } catch (UnknownHostException e1) {
            e1.printStackTrace();
        }

        return "http://" + ip + ":8050/files/recordfile/" + keyName;
    }

    @Override
    public void ExcelDownload(String fileName, List<?> data, List<String> title) throws IOException {
        XSSFWorkbook wb = new XSSFWorkbook();
        XSSFSheet sheet = wb.createSheet("Data");
        CreationHelper createHelper = wb.getCreationHelper();
        XSSFRow row = sheet.createRow(0);
        XSSFCellStyle cellStyle1 = wb.createCellStyle();
        cellStyle1.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        applyCellStyle(cellStyle1);
        XSSFCellStyle cellStyle2 = wb.createCellStyle();
        cellStyle2.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        applyCellStyle(cellStyle2);
        AtomicInteger ai = new AtomicInteger();
        List<String> keySet = new ArrayList<>();
        for (String e : title) {
            XSSFCell c = row.createCell(ai.getAndIncrement());
            c.setCellValue(e);
            c.setCellStyle(cellStyle1);
        }

        // 컬럼 수 만큼 컬럼 width 기본 값으로 세팅
        for (int i=0; i <keySet.size() ; i++ ) {
            sheet.setColumnWidth(i, 10 * 256 );
        }

        for (Field e : data.get(0).getClass().getDeclaredFields()) {
            keySet.add(e.getName());
        }
        row.setHeight((short) 400);
        AtomicInteger ai1 = new AtomicInteger();

        for (Object e : data) {
            XSSFRow row1 = sheet.createRow(ai1.incrementAndGet());
            AtomicInteger ai2 = new AtomicInteger();
            JSONObject obj = new JSONObject(e);
            for (String f : keySet) {
                int rowNum = ai2.getAndIncrement();
                if (obj.names().toList().contains(f)) {
                    XSSFCell c = row1.createCell(rowNum);
                    c.setCellValue(createHelper.createRichTextString(obj.get(f).toString()));
                    c.setCellStyle(cellStyle2);
                } else {
                    XSSFCell c = row1.createCell(rowNum);
                    c.setCellValue(createHelper.createRichTextString(""));
                    c.setCellStyle(cellStyle2);
                }

                row1.setHeight((short) 400);
            }
        }

        // data 1000건 이하일때 컬럼 autoSize + 여백 추가 (row 수가 많을 경우 성능 문제가 있음)
        if (data.size() < 1000) {
            for (int i=0; i <keySet.size() ; i++ ) {
                sheet.autoSizeColumn(i);
                sheet.setColumnWidth(i, (sheet.getColumnWidth(i))+512 );
            }
        }

        try (ByteArrayOutputStream bout = new ByteArrayOutputStream()) {
            wb.write(bout);
            bout.close();
            httpServletResponse.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=EUC-KR");
            httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx; charset=EUC-KR");
            ServletOutputStream out = httpServletResponse.getOutputStream();
            out.write(bout.toByteArray());
            out.flush();
            out.close();
        }
    }

    private void applyCellStyle(XSSFCellStyle cellStyle) {
        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        cellStyle.setBorderBottom(BorderStyle.THIN);
    }
}
