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 com.caliverse.admin.global.common.constants.CommonConstants;
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 List<Excel> getListData(MultipartFile file){
        List<Excel> excelList = new ArrayList<Excel>();

        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;

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

                if(row == null || row.getCell(0).toString().isBlank()) continue;

                Excel rowData = new Excel();

                Cell userCell = row.getCell(0);
                Cell typeCell = row.getCell(1);

                if(getCellValue(typeCell).equals(CommonConstants.GUID)){
                    if(!isString32Characters(getCellValue(userCell))){
                        log.error("getListData : Excel Upload Guid Check Fail type {}, user {}", typeCell, userCell);
                        throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.GUID_LENGTH_CHECK.getMessage() );   //guid 32자 체크
                    }
                }else if(getCellValue(typeCell).equals(CommonConstants.NICKNAME) || getCellValue(typeCell).equals(CommonConstants.EMAIL)){
                }else{
                    log.error("getListData : Excel Upload Type Error type {}, user {}", typeCell, userCell);
                    throw new RestApiException(CommonCode.ERROR.getHttpStatus(), ErrorCode.USERTYPE_CHECK_EXCEL.getMessage() );
                }

                rowData.setUser(getCellValue(userCell));
                rowData.setType(getCellValue(typeCell));

                excelList.add(rowData);
            }

        } 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 boolean hasDuplicatesExcel(List<Excel> dataList) {
        Set<String> uniqueValues = new HashSet<>();

        for (Excel excel : dataList) {
            if(uniqueValues.contains(excel.getUser())){
                return true;
            }
            uniqueValues.add(excel.getUser());
        }

        return false;
    }

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


}
