package com.caliverse.admin.global.common.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.eval.ErrorEval;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import com.caliverse.admin.domain.entity.Currencys;
import com.caliverse.admin.domain.entity.ROUTE;
import com.caliverse.admin.domain.entity.Excel;
import com.caliverse.admin.domain.response.IndicatorsResponse;
import com.caliverse.admin.global.common.code.CommonCode;
import com.caliverse.admin.global.common.code.ErrorCode;
import com.caliverse.admin.global.common.exception.RestApiException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;

import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;

@Slf4j
@Component
public class ExcelUtils {

    @Value("${excel.file-path}")
    private String filePath;
    // 각 셀의 데이터타입에 맞게 값 가져오기
    public String getCellValue(Cell cell) {

        String value = "";

        if(cell == null){
            return value;
        }

        switch (cell.getCellType()) {
            case STRING: // getRichStringCellValue() 메소드를 사용하여 컨텐츠를 읽음
                value = cell.getRichStringCellValue().getString();
                break;
            case NUMERIC: // 날짜 또는 숫자를 포함 할 수 있으며 아래와 같이 읽음
                if (DateUtil.isCellDateFormatted(cell))
                    value = cell.getLocalDateTimeCellValue().toString();
                else
                    value = String.valueOf(cell.getNumericCellValue());
                if (value.endsWith(".0"))
                    value = value.substring(0, value.length() - 2);
                break;
            case BOOLEAN:
                value = String.valueOf(cell.getBooleanCellValue());
                break;
            case FORMULA:
                value = String.valueOf(cell.getCellFormula());
                break;
            case ERROR:
                value = ErrorEval.getText(cell.getErrorCellValue());
                break;
            case BLANK:
            case _NONE:
            default:
                value = "";
        }
        return value;
    }

    public Workbook loadExcel(String fileName){
        String fullPath = filePath + fileName;

        try (FileInputStream fis = new FileInputStream(new File(fullPath))) {
            return WorkbookFactory.create(fis);
        }catch(Exception e){
            log.error(e.getMessage());
        }
        return null;
    }

    // 엑셀파일의 데이터 목록 가져오기 (파일명기준)
    public List<Excel> getExcelListData(String fileName){

        List<Excel> excelList = new ArrayList<>();

        try {
            Workbook workbook = loadExcel(fileName);

            // 첫번째 시트
            Sheet sheet = workbook.getSheetAt(0);

            int rowIndex = 0;

            // 첫번째 행(0)은 컬럼 명이기 때문에 두번째 행(1) 부터 검색
            for (rowIndex = 1; rowIndex < sheet.getLastRowNum() + 1; rowIndex++) {
                Row row = sheet.getRow(rowIndex);

                // 빈 행은 Skip
                if (row != null && row.getCell(0) != null && !row.getCell(0).toString().isBlank()) {

                    Cell cell = row.getCell(0);
                    Cell cell2 = row.getCell(1);

                    Excel excel = Excel.builder().user(getCellValue(cell)).type(getCellValue(cell2)).build();
                    excelList.add(excel);
                }
            }

        } catch (Exception e) {
            log.error("Exception Excel Format:" + e.getMessage());
        }

        return excelList;
    }

    // 엑셀파일의 데이터 목록 가져오기 (파라미터들은 위에서 설명함)
    public List<String> getListData(MultipartFile file, int startRowNum, int columnLength){

        List<String> excelList = new ArrayList<String>();

        try {
            Workbook workbook = null;

            String ext = FilenameUtils.getExtension(file.getOriginalFilename()).toLowerCase();
            if(!ext.equals("xlsx") && !ext.equals("xls")){
                throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.NOT_EXIT_EXCEL.getMessage() );   //Excel 파일을 선택해주세요.
            }
            if (ext.equals("xls")){
                workbook = new HSSFWorkbook(file.getInputStream());
            }else{
                workbook = new XSSFWorkbook(file.getInputStream());
            }

            // 첫번째 시트
            Sheet sheet = workbook.getSheetAt(0);

            int rowIndex = 0;
            int columnIndex = 0;

            // 첫번째 행(0)은 컬럼 명이기 때문에 두번째 행(1) 부터 검색
            for (rowIndex = startRowNum; rowIndex < sheet.getLastRowNum() + 1; rowIndex++) {
                Row row = sheet.getRow(rowIndex);

                // 빈 행은 Skip
                if (row != null && row.getCell(0) != null && !row.getCell(0).toString().isBlank()) {

                    Map<String, Object> map = new HashMap<String, Object>();

                    int cells = columnLength;

                    for (columnIndex = 0; columnIndex <= cells; columnIndex++) {
                        Cell cell = row.getCell(columnIndex);
                        Cell cell2 = row.getCell(columnIndex+1);
                        if(getCellValue(cell2).equals("GUID")){
                            if(isString32Characters(getCellValue(cell))){
                                excelList.add(getCellValue(cell));
                            }else{
                                log.error("getListData : Excel Upload Guid Check Fail type {}, user {}", cell2, cell);
                                throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.GUID_LENGTH_CHECK.getMessage() );   //guid 32자 체크
                            }
                        }else if(getCellValue(cell2).equals("NICKNAME")){
                            excelList.add(getCellValue(cell));
                        }else if(getCellValue(cell2).equals("EMAIL")){
                            excelList.add(getCellValue(cell));
                        }else{
                            log.error("getListData : Excel Upload Type Error type {}, user {}", cell2, cell);
                            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.USERTYPE_CHECK_EXCEL.getMessage() );
                        }
                        /*map.put(String.valueOf(columnIndex), getCellValue(cell));
                        log.info(rowIndex + " 행 : " + columnIndex+ " 열 = " + getCellValue(cell));*/
                    }

                    //excelList.add(map);
                }
            }

        } catch (InvalidFormatException e) {
            log.info("getListData Invalid Excel Format:" + e.getMessage());
        } catch (IOException e) {
            log.info("getListData IOException Excel Format:" + e.getMessage());
        }

        return excelList;
    }

    public void excelDownload(String sheetName, String headerNames[], String bodyDatass[][],String outfileName
            , HttpServletResponse res) throws IOException {

        Workbook workbook = new XSSFWorkbook();
        ServletOutputStream servletOutputStream = res.getOutputStream();
        try {

            Sheet sheet = workbook.createSheet(sheetName); // 엑셀 sheet 이름

            /**
             * header font style
             */
            XSSFFont headerXSSFFont = (XSSFFont) workbook.createFont();
            headerXSSFFont.setColor(new XSSFColor(new byte[]{(byte) 255, (byte) 255, (byte) 255}));

            /**
             * header cell style
             */
            XSSFCellStyle headerXssfCellStyle = (XSSFCellStyle) workbook.createCellStyle();

            // 테두리 설정
            headerXssfCellStyle.setBorderLeft(BorderStyle.THIN);
            headerXssfCellStyle.setBorderRight(BorderStyle.THIN);
            headerXssfCellStyle.setBorderTop(BorderStyle.THIN);
            headerXssfCellStyle.setBorderBottom(BorderStyle.THIN);

            // 배경 설정
        /*headerXssfCellStyle.setFillForegroundColor(new XSSFColor(new byte[]{(byte) 34, (byte) 37, (byte) 41}));
        headerXssfCellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        headerXssfCellStyle.setFont(headerXSSFFont);*/

            /**
             * body cell style
             */
            XSSFCellStyle bodyXssfCellStyle = (XSSFCellStyle) workbook.createCellStyle();

            // 테두리 설정
            bodyXssfCellStyle.setBorderLeft(BorderStyle.THIN);
            bodyXssfCellStyle.setBorderRight(BorderStyle.THIN);
            bodyXssfCellStyle.setBorderTop(BorderStyle.THIN);
            bodyXssfCellStyle.setBorderBottom(BorderStyle.THIN);

            /**
             * header data
             */
            int rowCount = 0; // 데이터가 저장될 행

            Row headerRow = null;
            Cell headerCell = null;

            headerRow = sheet.createRow(rowCount++);
            for(int i=0; i<headerNames.length; i++) {
                headerCell = headerRow.createCell(i);
                headerCell.setCellValue(headerNames[i]); // 데이터 추가
                headerCell.setCellStyle(headerXssfCellStyle); // 스타일 추가
            }

            Row bodyRow = null;
            Cell bodyCell = null;

            for(String[] bodyDatas : bodyDatass) {
                bodyRow = sheet.createRow(rowCount++);

                for(int i=0; i<bodyDatas.length; i++) {
                    bodyCell = bodyRow.createCell(i);
                    bodyCell.setCellValue(bodyDatas[i]); // 데이터 추가
                    bodyCell.setCellStyle(bodyXssfCellStyle); // 스타일 추가
                    sheet.autoSizeColumn(i);
                    sheet.setColumnWidth(i, (sheet.getColumnWidth(i))+1024); //너비 더 넓게
                }
            }

            /**
             * download
             */
            res.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            res.setHeader("Content-Disposition", "attachment;filename=" + outfileName + ".xlsx");

            workbook.write(servletOutputStream);
            workbook.close();
            servletOutputStream.flush();
            servletOutputStream.close();
        }catch (IOException exception){
            exception.getMessage();
        }finally {
            workbook.close();
            servletOutputStream.flush();
            servletOutputStream.close();
        }
    }

    public String saveExcelFile(MultipartFile file){
        try{
            String fileName = CommonUtils.setFileName(file.getOriginalFilename());
            String path = filePath + File.separator + fileName;
            Path destinationPath = Path.of(path);

            Files.copy(file.getInputStream(), destinationPath, StandardCopyOption.REPLACE_EXISTING);

            return fileName;
        }catch (IOException e){
            throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.ERROR_EXCEL_UPLOAD.getMessage());
        }
    }


    public boolean hasDuplicates(List<String> dataList) {
        Set<String> uniqueValues = new HashSet<>();

        for (String value : dataList) {
            if (uniqueValues.contains(value)) {
                return true;
            }
            uniqueValues.add(value);
        }

        return false;
    }

    public static boolean isString32Characters(String input) {
        if (input == null) {
            return false;
        }
        return input.length() == 32;
    }

    //재화 지표 엑셀 다운 샘플
    public static void exportToExcelByDailyGoods(List<IndicatorsResponse.DailyGoods> dailyGoodsList
            ,HttpServletResponse res) throws IOException {
        ServletOutputStream servletOutputStream = res.getOutputStream();
        try (Workbook workbook = new XSSFWorkbook()) {
            // 엑셀 시트 생성
            Sheet sheet = workbook.createSheet("DailyGoodsData");

            // 첫 번째 라인 - 일자
            Row dateRow = sheet.createRow(0);
            dateRow.createCell(0).setCellValue("일자");
            sheet.autoSizeColumn(0);
            sheet.setColumnWidth((0), (sheet.getColumnWidth(0))+1024*2); //너비 더 넓게
            for (int i = 0; i < dailyGoodsList.size(); i++) {
                dateRow.createCell((i+1) + 1).setCellValue(dailyGoodsList.get(i).getDate().toString());
                //sheet 넓이 auto
                sheet.autoSizeColumn((i+2));
            }

            // 두 번째 라인 - ACQUIRE 총량
            Row acquireRow = sheet.createRow(1);
            acquireRow.createCell(0).setCellValue("(Total)생산량");

            for (int i = 0; i < dailyGoodsList.size(); i++) {
                List<IndicatorsResponse.Total> totals = dailyGoodsList.get(i).getTotal();
                for (int j = 0; j < totals.size(); j++) {
                    if ("ACQUIRE".equals(totals.get(j).getDeltaType())) {
                        acquireRow.createCell((i+1) + 1).setCellValue(totals.get(j).getQuantity());
                    }
                }
            }
            // 세번째 라인 - CONSUME 총량
            Row consumeRow = sheet.createRow(2);
            consumeRow.createCell(0).setCellValue("(Total)소진량");

            for (int i = 0; i < dailyGoodsList.size(); i++) {
                List<IndicatorsResponse.Total> totals = dailyGoodsList.get(i).getTotal();
                for (int j = 0; j < totals.size(); j++) {
                    if ("CONSUME".equals(totals.get(j).getDeltaType())) {
                        consumeRow.createCell((i+1) + 1).setCellValue(totals.get(j).getQuantity());
                    }
                }
            }


            // 나머지 라인 - Route 별 quantity
            List<ROUTE> acquireRoutes = Arrays.asList(
                    ROUTE.QUEST_REWARD, ROUTE.SEASON_PASS, ROUTE.FROM_PROP,
                    ROUTE.CLAIM_REWARD, ROUTE.USE_ITEM, ROUTE.TATTOO_ENHANCE,
                    ROUTE.TATTOO_CONVERSION, ROUTE.SHOP_BUY, ROUTE.SHOP_SELL,
                    ROUTE.PAID_PRODUCT, ROUTE.TRADE_ADD, ROUTE.NFT_LOCKIN
            );
            int rowIndex = 3; // 시작 행 인덱스

            for (ROUTE route : acquireRoutes) {
                Row routeRow = sheet.createRow(rowIndex++);
                routeRow.createCell(0).setCellValue("GET");
                routeRow.createCell(1).setCellValue(route.getDescription());

                for (int i = 0; i < dailyGoodsList.size(); i++) {
                    List<IndicatorsResponse.DailyData> dailyDataList = dailyGoodsList.get(i).getDailyData();
                    for (IndicatorsResponse.DailyData dailyData : dailyDataList) {
                        if(dailyData.getDeltaType().equals("ACQUIRE")){
                            List<Currencys> data = dailyData.getData();
                            for (Currencys currencys : data) {
                                if (route.getDescription().equals(currencys.getRoute())) {
                                    routeRow.createCell((i+1) + 1).setCellValue(currencys.getQuantity());
                                }
                            }
                        }
                    }
                }
            }
            List<ROUTE> consumeRoutes = Arrays.asList(
                    ROUTE.SHOP_BUY, ROUTE.SHOP_SELL, ROUTE.USE_PROP,
                    ROUTE.USE_TAXI, ROUTE.TATTOO_ENHANCE, ROUTE.TATTOO_CONVERSION,
                    ROUTE.SHOUT, ROUTE.SUMMON, ROUTE.CREATE_PARTYROOM,
                    ROUTE.INVENTORY_EXPAND, ROUTE.TRADE_REMOVE, ROUTE.NFT_UNLOCK
            );
            for (ROUTE route : consumeRoutes) {
                Row routeRow = sheet.createRow(rowIndex++);
                routeRow.createCell(0).setCellValue("USE");
                routeRow.createCell(1).setCellValue(route.getDescription());

                for (int i = 0; i < dailyGoodsList.size(); i++) {
                    List<IndicatorsResponse.DailyData> dailyDataList = dailyGoodsList.get(i).getDailyData();
                    for (IndicatorsResponse.DailyData dailyData : dailyDataList) {
                        if(dailyData.getDeltaType().equals("CONSUME")){
                            List<Currencys> data = dailyData.getData();
                            for (Currencys currencys : data) {
                                if (route.getDescription().equals(currencys.getRoute())) {
                                    routeRow.createCell((i+1) + 1).setCellValue(currencys.getQuantity());
                                }
                            }
                        }
                    }
                }
            }
            //cell 머지
            sheet.addMergedRegion(CellRangeAddress.valueOf("A1:B1"));
            sheet.addMergedRegion(CellRangeAddress.valueOf("A2:B2"));
            sheet.addMergedRegion(CellRangeAddress.valueOf("A3:B3"));
            sheet.addMergedRegion(CellRangeAddress.valueOf("A4:A15"));
            sheet.addMergedRegion(CellRangeAddress.valueOf("A16:A27"));
            /**
             * download
             */
            res.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            res.setHeader("Content-Disposition", "attachment;filename=DailyGoodsData_" + LocalDateTime.now().getNano()
                    + ".xlsx");

            workbook.write(servletOutputStream);
            workbook.close();
            servletOutputStream.flush();
            servletOutputStream.close();
        } catch (IOException e) {
            log.error("exportToExcelByDailyGoods IOException: {}", e.getMessage());
        }
    }
    //유저 지표 Retention 엑셀 다운 샘플
    public static void exportToExcelByRentention(List<IndicatorsResponse.Retention> list
            ,HttpServletResponse res) throws IOException {
        ServletOutputStream servletOutputStream = res.getOutputStream();
        try (Workbook workbook = new XSSFWorkbook()) {
            // 엑셀 시트 생성
            Sheet sheet = workbook.createSheet("Retention");

            // 첫 번째 라인 - 일자
            Row dateRow = sheet.createRow(0);
            dateRow.createCell(0).setCellValue("국가");
            dateRow.createCell(1).setCellValue("일자");
            dateRow.createCell(2).setCellValue("NRU");

            for (int i = 1; i < list.size(); i++) {
                dateRow.createCell(i+2).setCellValue("D+"+i);
                //sheet 넓이 auto
                sheet.autoSizeColumn(i+2);
            }

            int rowIndex = 1; // 시작 행 인덱스
            // 두 번째 라인
            for (IndicatorsResponse.Retention retention : list){
                Row row = sheet.createRow(rowIndex++);
                row.createCell(0).setCellValue("ALL");
                List<IndicatorsResponse.Dday> dDay = retention.getDDay();
                row.createCell(1).setCellValue(retention.getDate().toString());
                for (int j = 0; j < dDay.size(); j++) {
                    row.createCell(j+2).setCellValue(dDay.get(j).getDif());
                }

            }

            //cell 머지
            sheet.addMergedRegion(CellRangeAddress.valueOf("A2:A"+(list.size()+1)));
            /*sheet.addMergedRegion(CellRangeAddress.valueOf("A1:B1"));
            sheet.addMergedRegion(CellRangeAddress.valueOf("A2:B2"));
            sheet.addMergedRegion(CellRangeAddress.valueOf("A3:B3"));
            sheet.addMergedRegion(CellRangeAddress.valueOf("A4:A15"));
            sheet.addMergedRegion(CellRangeAddress.valueOf("A16:A27"));*/
            /**
             * download
             */
            res.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            res.setHeader("Content-Disposition", "attachment;filename=Retention_" + LocalDateTime.now().getNano()
                    + ".xlsx");

            workbook.write(servletOutputStream);
            workbook.close();
            servletOutputStream.flush();
            servletOutputStream.close();
        } catch (IOException e) {
            log.error("exportToExcelByRentention IOException: {}", e.getMessage());
        }
    }
    //유저 지표 Retention 엑셀 다운 샘플
    public static void exportToExcelBySegment(List<IndicatorsResponse.Segment> list,LocalDate startDt,
            LocalDate endDt,HttpServletResponse res) throws IOException {
        ServletOutputStream servletOutputStream = res.getOutputStream();
        try (Workbook workbook = new XSSFWorkbook()) {
            // 엑셀 시트 생성
            Sheet sheet = workbook.createSheet("Segment");
            sheet.autoSizeColumn(0);
            sheet.autoSizeColumn(1);
            sheet.autoSizeColumn(2);
            sheet.addMergedRegion(CellRangeAddress.valueOf("B1:C1"));
            sheet.setColumnWidth(0,sheet.getColumnWidth(0)+1024*4);


            int rowIndex = 0;
            // 첫 번째 라인 - 일자
            Row headerRow = sheet.createRow(rowIndex++);
            headerRow.createCell(0).setCellValue(startDt.toString()+"~"+endDt.toString());
            headerRow.createCell(1).setCellValue("KIP");

            Row subHeaderRow = sheet.createRow(rowIndex++);
            subHeaderRow.createCell(0).setCellValue("세그먼트 분류");
            subHeaderRow.createCell(1).setCellValue("AU");
            subHeaderRow.createCell(2).setCellValue("AU Percentage by User Segment (%)");

            Row dataRow = null;
            for (int i = 0; i < list.size(); i++) {
                dataRow = sheet.createRow(i+rowIndex);
                dataRow.createCell(0).setCellValue(list.get(i).getType());
                dataRow.createCell(1).setCellValue(list.get(i).getAu());
                dataRow.createCell(2).setCellValue(list.get(i).getDif());
            }

            //cell 머지
            /*sheet.addMergedRegion(CellRangeAddress.valueOf("A1:B1"));
            sheet.addMergedRegion(CellRangeAddress.valueOf("A2:B2"));
            sheet.addMergedRegion(CellRangeAddress.valueOf("A3:B3"));
            sheet.addMergedRegion(CellRangeAddress.valueOf("A4:A15"));
            sheet.addMergedRegion(CellRangeAddress.valueOf("A16:A27"));*/
            /**
             * download
             */
            res.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            res.setHeader("Content-Disposition", "attachment;filename=Segment_" + LocalDateTime.now().getNano()
                    + ".xlsx");

            workbook.write(servletOutputStream);
            workbook.close();
            servletOutputStream.flush();
            servletOutputStream.close();
        } catch (IOException e) {
            log.error("exportToExcelBySegment IOException: {}", e.getMessage());
        }
    }


}
