package com.tsi.monitoring.service;

import com.tsi.monitoring.mapper.section.SectionMapper;
import com.tsi.monitoring.mapper.utis.UtisMapper;
import com.tsi.monitoring.vo.*;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
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.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor
@Slf4j
public class ReportService {
    private final SectionMapper sectionMapper;
    private final UtisMapper utisMapper;
    private final Map<String, String[]> headerMap = new HashMap<>();
    private final Map<String, String> sheetMap = new HashMap<>();
    private final Map<String, TriFunction<Map<String, Object>,
            String[], String, Workbook>> methodMap = new HashMap<>();

    @PostConstruct
    private void init() {
        initializeReportMap();
    }

    /**
     * Tree Param Function
     * @param <T> Map<String, Object> param
     * @param <U> String[] headers
     * @param <V> String sheetName
     * @param <R> return WorkBook
     */
    @FunctionalInterface
    public interface TriFunction<T, U, V, R> {
        R apply(T t, U u, V v);
    }

    /**
     * Initialize Report Info
     * 추후 리포트 생성 있을 시 여기에 header, sheetName, workbook 생성 메서드 추가
     * Key 값은 type - /api/report/{type}/download
     */
    public void initializeReportMap() {
        String[] incdHeader   = { "수집시각", "돌발 수집 건수", "공사/행사 수집 건수", "돌발+공사/행사" };
        String[] centerHeader = { "일시", "소통정보 수신", "돌발정보 수신", "소통정보 송신", "돌발정보 송신", "민간교통정보 송신" };
        String[] centerCntHeader = { "전송시작일시", "전송종료일시", "소통정보 수신횟수", "소통정보 수신건수",
                                        "소통정보 송신횟수", "소통정보 송신건수", "민간교통정보 송신횟수", "민간교통정보 송신건수" };
        String[] ntsHeader    = { "수집시각", "교통정보 수집 건수" };
        String[] moctHeader   = { "전송시각", "교통정보", "돌발정보" };
        String[] sndMHeader   = { "전송시각", "수집건수", "전송건수", "수집-전송차이" };
        String[] rcvMHeader   = { "수집시각", "수집건수" };
        String[] sigCHeader   = { "수집시각", "수집일련번호", "통신코드", "통신코드 내용" };

        this.headerMap.put("incd", incdHeader);
        this.headerMap.put("center", centerHeader);
        this.headerMap.put("snd_rcv_center", centerCntHeader);
        this.headerMap.put("nts", ntsHeader);
        this.headerMap.put("moct", moctHeader);
        this.headerMap.put("snd_molit", sndMHeader);
        this.headerMap.put("rcv_molit", rcvMHeader);
        this.headerMap.put("rcv_moct", rcvMHeader);
        this.headerMap.put("sig_comm", sigCHeader);

        this.sheetMap.put("incd", "도로공사 돌발정보 수집 이력");
        this.sheetMap.put("center", "지역센터 제공-수집정보 이력");
        this.sheetMap.put("snd_rcv_center", "지역센터 일별 제공-수집 전송 이력");
        this.sheetMap.put("nts", "동부 NTS 교통정보 수집 이력");
        this.sheetMap.put("moct", "민간 교통정보 제공 이력");
        this.sheetMap.put("snd_molit", "국토부 교통정보 제공 이력");
        this.sheetMap.put("rcv_molit", "국토부 교통정보 수집 이력");
        this.sheetMap.put("rcv_moct", "국토부 민간정보 제공 이력");
        this.sheetMap.put("sig_comm", "지역센터 신호연계 통신 이력");

        this.methodMap.put("incd", this::getIncdWorkBook);
        this.methodMap.put("center", this::getCenterWorkBook);
        this.methodMap.put("snd_rcv_center", this::getCenterCntWorkBook);
        this.methodMap.put("nts", this::getNtsWorkBook);
        this.methodMap.put("moct", this::getMoctWorkBook);
        this.methodMap.put("snd_molit", this::getSndMolitWorkBook);
        this.methodMap.put("rcv_molit", this::getRcvMolitWorkBook);
        this.methodMap.put("rcv_moct", this::getRcvMoctWorkBook);
        this.methodMap.put("sig_comm", this::getSigCommWorkBook);
    }

    /**
     * Send To Binary Excel file
     * @param type report type
     * @param paramMap param
     * @return Excel ResponseEntity
     */
    public ResponseEntity<byte[]> getReport(String type, Map<String, Object> paramMap) {
        String[] headers = this.headerMap.get(type);
        String sheetName = this.sheetMap.get(type);
        Workbook workbook = this.methodMap.get(type).apply(paramMap, headers, sheetName);
        return excelDown(workbook, sheetName);
    }

    /**
     * WorkBook to ResponseEntity<byte[]>
     * @param workbook Workbook
     * @param sheetName SheetName
     * @return ResponseEntity<byte[]>
     */
    public ResponseEntity<byte[]> excelDown(Workbook workbook, String sheetName) {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            workbook.write(bos);
            HttpHeaders headersHttp = new HttpHeaders();
            headersHttp.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headersHttp.setContentDisposition(ContentDisposition.attachment().filename(sheetName, StandardCharsets.UTF_8).build());
            return new ResponseEntity<>(bos.toByteArray(), headersHttp, HttpStatus.OK);
        }
        catch(IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    /**
     * Excel Default Style
    */
    private void setDefaultStyle(CellStyle style) {
        //셀 얇은 선 생성
        style.setBorderBottom(BorderStyle.THIN);
        style.setBorderTop(BorderStyle.THIN);
        style.setBorderLeft(BorderStyle.THIN);
        style.setBorderRight(BorderStyle.THIN);

        //색상 채우기 기능
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);

        //가운데 정렬
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setAlignment(HorizontalAlignment.CENTER);
    }

    /**
     * XSSFWorkbook Default Style Setting
     * @param sheetName String
     * @param headers String[]
     * @param listSize long
     * @return Workbook
     */
    public Workbook makeExcel(String sheetName, String[] headers, long listSize) {
        XSSFWorkbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet(sheetName);

        XSSFCellStyle headerStyle = createCellStyle(workbook, new java.awt.Color(240, 239, 239), true);
        XSSFCellStyle oddStyle = createCellStyle(workbook, new java.awt.Color(245, 245, 253), false);
        XSSFCellStyle evenStyle = createCellStyle(workbook, new java.awt.Color(255, 247, 236), false);


        Row headerRow = sheet.createRow(0);

        for (int ii = 0; ii < headers.length; ii++) {
            sheet.setColumnWidth(ii, (ii == 0 ? 30 : 20) * 256);  // 수집시각
            Cell cell = headerRow.createCell(ii);
            cell.setCellValue(headers[ii]);
            cell.setCellStyle(headerStyle);
        }

        for (int ii = 0; ii < listSize; ii++) {
            Row row = sheet.createRow(ii + 1);
            XSSFCellStyle style = (ii + 1) % 2 == 0 ? evenStyle : oddStyle;
            for (int jj = 0; jj < headers.length; jj++) {
                row.createCell(jj).setCellStyle(style);
            }
        }

        Row totalRow = sheet.createRow((int) listSize + 1);
        for (int ii = 0; ii < headers.length; ii++) {
            totalRow.createCell(ii);
            totalRow.getCell(ii).setCellStyle(headerStyle);
        }

        return workbook;
    }

    /**
     * Setting Row Cell Value
     * @param row Worksheet Row
     * @param values Row Values
     */
    private void setCellValues(Row row, Object... values) {
        for (int i = 0; i < values.length; i++) {
            if (row.getCell(i) == null) row.createCell(i);
            if (values[i] instanceof String str) {
                row.getCell(i).setCellValue(str);
            } else if (values[i] instanceof Number num) {
                row.getCell(i).setCellValue(num.doubleValue());
            } else {
                row.getCell(i).setCellValue(String.valueOf(values[i]));
            }
        }
    }

    /**
     * Excel Cell Style
     * @param workbook Workbook
     * @param color Cell Color
     * @param bold isBold
     * @return style
     */
    private XSSFCellStyle createCellStyle(XSSFWorkbook workbook, java.awt.Color color, boolean bold) {
        XSSFCellStyle style = workbook.createCellStyle();
        style.setFillForegroundColor(new XSSFColor(color, null));
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        setDefaultStyle(style);
        if (bold) {
            XSSFFont font = workbook.createFont();
            font.setBold(true);
            style.setFont(font);
        }
        return style;
    }

    /**
     * 도로공사 돌발정보 수집이력
     * @param paramMap Parameter
     * @param headers Excel Header
     * @param sheetName Excel Sheet Name
     * @return Workbook
     */
    public Workbook getIncdWorkBook(Map<String, Object> paramMap, String[] headers, String sheetName) {
        int rowNum = 1;
        List<RcvIncdMoctVo> list = this.utisMapper.getPeriodRecIncdHistory(paramMap);
        Workbook workbook = makeExcel(sheetName, headers, list.size());
        Sheet sheet = workbook.getSheet(sheetName);
        long[] totals = new long[headers.length - 1];
        for (RcvIncdMoctVo vo : list) {
            Row row = sheet.getRow(rowNum++);
            long incdCnt = Long.parseLong(vo.getIncdCnt());
            long evpCnt = Long.parseLong(vo.getEvtCnt());
            long sumCnt = Long.parseLong(vo.getSumCnt());
            setCellValues(row, vo.getRegDate(), incdCnt, evpCnt, sumCnt);
            totals[0] += incdCnt;
            totals[1] += evpCnt;
            totals[2] += sumCnt;
        }
        Row totalRow = sheet.getRow(rowNum);
        setCellValues(totalRow, "합계", totals[0], totals[1], totals[2]);
        return workbook;
    }

    /**
     * 지역센터 제공/수집정보 이력
     * @param paramMap Parameter
     * @param headers Excel Header
     * @param sheetName Excel Sheet Name
     * @return Workbook
     */
    public Workbook getCenterWorkBook(Map<String, Object> paramMap, String[] headers, String sheetName) {
        int rowNum = 1;
        List<PeriodCenterRcvSendStatisticsVo> list = this.sectionMapper.getPeriodCenterRcvSendStatistics(paramMap);
        Workbook workbook = makeExcel(sheetName, headers, list.size());
        Sheet sheet = workbook.getSheet(sheetName);
        long[] totals = new long[headers.length - 1];
        for (PeriodCenterRcvSendStatisticsVo vo : list) {
            Row row = sheet.getRow(rowNum++);
            totals[0] += vo.getRTCnt();
            totals[1] += vo.getRICnt();
            totals[2] += vo.getRCCnt();
            totals[3] += vo.getSTCnt();
            totals[4] += vo.getSICnt();
            totals[5] += vo.getSCCnt();
            totals[6] += vo.getSMCnt();

            setCellValues(row,
                    vo.getLogDate(), vo.getRTCnt(), vo.getRICnt(),
                    vo.getRCCnt(), vo.getSTCnt(), vo.getSICnt(),
                    vo.getSCCnt(), vo.getSMCnt()
            );
        }
        Row totalRow = sheet.getRow(rowNum);
        if (totalRow == null) totalRow = sheet.createRow(rowNum);
        setCellValues(totalRow, "합계", totals[0], totals[1], totals[2], totals[3],
                totals[4], totals[5], totals[6]);
        return workbook;
    }


    /**
     * 지역센터 일별 제공/수집 전송 이력
     * @param paramMap Parameter
     * @param headers Excel Header
     * @param sheetName Excel Sheet Name
     * @return Workbook
     */
    public Workbook getCenterCntWorkBook(Map<String, Object> paramMap, String[] headers, String sheetName) {
        int rowNum = 1;
        List<CenterSendRecCountVo> list = this.utisMapper.getCenterSendRecCount(paramMap);
        Workbook workbook = makeExcel(sheetName, headers, list.size());
        Sheet sheet = workbook.getSheet(sheetName);
        long[] totals = new long[headers.length - 1];
        for (CenterSendRecCountVo vo : list) {
            Row row = sheet.getRow(rowNum++);
            totals[0] += vo.getRecvCount();
            totals[1] += vo.getRecvDataCnt();
            totals[2] += vo.getSendCount();
            totals[3] += vo.getSendDataCnt();
            totals[4] += vo.getMoctCount();
            totals[5] += vo.getMoctDataCnt();

            setCellValues(row,
                vo.getStartTime(), vo.getEndTime(), vo.getRecvCount(), vo.getRecvDataCnt(),
                vo.getSendCount(), vo.getSendDataCnt(), vo.getMoctCount(),
                vo.getMoctDataCnt()
            );
        }
        Row totalRow = sheet.getRow(rowNum);
        if (totalRow == null) totalRow = sheet.createRow(rowNum);
        setCellValues(totalRow, "합계", "", totals[0], totals[1], totals[2], totals[3],
                totals[4], totals[5]);
        return workbook;
    }


    /**
     * 동부 NTS 교통정보 수집 이력
     * @param paramMap Parameter
     * @param headers Excel Header
     * @param sheetName Excel Sheet Name
     * @return Workbook
     */
    public Workbook getNtsWorkBook(Map<String, Object> paramMap, String[] headers, String sheetName) {
        int rowNum = 1;
        List<SendRcvHistVo> list = this.utisMapper.getPeriodRcvNtsStatistics(paramMap);
        Workbook workbook = makeExcel(sheetName, headers, list.size());
        Sheet sheet = workbook.getSheet(sheetName);
        long tot = 0;

        for (SendRcvHistVo vo : list) {
            Row row = sheet.getRow(rowNum++);
            tot += vo.getCnt();
            setCellValues(row, vo.getRegDate(), vo.getCnt());
        }

        Row totalRow = sheet.getRow(rowNum);
        if (totalRow == null) totalRow = sheet.createRow(rowNum);
        setCellValues(totalRow, "합계", tot);
        return workbook;
    }

    /**
     * 민간 교통정보 제공집 이력
     * @param paramMap Parameter
     * @param headers Excel Header
     * @param sheetName Excel Sheet Name
     * @return Workbook
     */
    public Workbook getMoctWorkBook(Map<String, Object> paramMap, String[] headers, String sheetName) {
        int rowNum = 1;
        List<SndMoctVo> list = this.utisMapper.getPeriodSndMoctStatistics(paramMap);
        Workbook workbook = makeExcel(sheetName, headers, list.size());
        Sheet sheet = workbook.getSheet(sheetName);
        long[] totals = new long[headers.length - 1];
        for (SndMoctVo vo : list) {
            Row row = sheet.getRow(rowNum++);
            setCellValues(row,
                    vo.getRegDate(), vo.getTraf(), vo.getIncd(), vo.getKbs(), vo.getSk(),
                    vo.getDongbu(), vo.getKbsIncd(), vo.getSkIncd(), vo.getDongbuIncd(), vo.getKbsCtrl()
            );
            totals[0] += vo.getTraf();
            totals[1] += vo.getIncd();
            totals[2] += vo.getKbs();
            totals[3] += vo.getSk();
            totals[4] += vo.getDongbu();
            totals[5] += vo.getKbsIncd();
            totals[6] += vo.getSkIncd();
            totals[7] += vo.getDongbuIncd();
            totals[8] += vo.getKbsCtrl();
        }
        Row totalRow = sheet.getRow(rowNum);
        if (totalRow == null) totalRow = sheet.createRow(rowNum);
        setCellValues(totalRow, "합계", totals[0], totals[1], totals[2], totals[3],
                totals[4], totals[5], totals[6], totals[7], totals[8]);
        return workbook;
    }

    /**
     * 국토부 교통정보 제공이력
     * @param paramMap Parameter
     * @param headers Excel Header
     * @param sheetName Excel Sheet Name
     * @return Workbook
     */
    public Workbook getSndMolitWorkBook(Map<String, Object> paramMap, String[] headers, String sheetName) {
        int rowNum = 1;
        String type = (String) paramMap.get("type");
        String end = "'";
        if (type.equals("hh")) {
            end = " HH24'";
        }

        paramMap.put("format", "'YYYY-MM-DD" + end);
        List<SndMolitTraffVo> list = this.utisMapper.getPeriodSndMolitTraffStatistics(paramMap);
        Workbook workbook = makeExcel(sheetName, headers, list.size());
        Sheet sheet = workbook.getSheet(sheetName);

        long[] totals = new long[headers.length - 1];
        for (SndMolitTraffVo vo : list) {
            Row row = sheet.getRow(rowNum++);
            setCellValues(row,vo.getLogDate(), vo.getRcvCnt());
            totals[0] += vo.getRcvCnt();
            totals[1] += vo.getSndCnt();
            totals[2] += vo.getSndGap();
        }
        Row totalRow = sheet.getRow(rowNum);
        if (totalRow == null) totalRow = sheet.createRow(rowNum);
        setCellValues(totalRow, "합계", totals[0], totals[1], totals[2]);
        return workbook;
    }

    /**
     * 국토부 교통정보 수집이력
     * @param paramMap Parameter
     * @param headers Excel Header
     * @param sheetName Excel Sheet Name
     * @return Workbook
     */
    public Workbook getRcvMolitWorkBook(Map<String, Object> paramMap, String[] headers, String sheetName) {
        int rowNum = 1;
        String type = (String) paramMap.get("type");
        List<SndMolitTraffVo> list;
        if (type.equals("hist")) {
            list =  this.utisMapper.getPeriodRcvMolitTraffHist(paramMap);
        }
        else {
            String end = type.equals("hh") ?  " HH24'" : "'";
            paramMap.put("format", "'YYYY-MM-DD" + end);

            list = this.utisMapper.getPeriodRcvMolitTraffStatistics(paramMap);
        }
        Workbook workbook = makeExcel(sheetName, headers, list.size());
        Sheet sheet = workbook.getSheet(sheetName);

        long[] totals = new long[headers.length - 1];
        for (SndMolitTraffVo vo : list) {
            Row row = sheet.getRow(rowNum++);
            setCellValues(row,vo.getLogDate(), vo.getRcvCnt());
            totals[0] += vo.getRcvCnt();
        }
        Row totalRow = sheet.getRow(rowNum);
        if (totalRow == null) totalRow = sheet.createRow(rowNum);
        setCellValues(totalRow, "합계", totals[0]);
        return workbook;
    }

    /**
     * 국토부 민간교통 수집이력
     * @param paramMap Parameter
     * @param headers Excel Header
     * @param sheetName Excel Sheet Name
     * @return Workbook
     */
    public Workbook getRcvMoctWorkBook(Map<String, Object> paramMap, String[] headers, String sheetName) {
        int rowNum = 1;
        String type = (String) paramMap.get("type");
        List<SndMolitTraffVo> list;
        if (type.equals("hist")) {
            list =   this.utisMapper.getPeriodRcvMoctTraffHist(paramMap);
        }
        else {
            String end = "'";
            if (type.equals("hh")) {
                end = " HH24'";
            }
            else if (type.equals("mm")) {
                end = " HH24:MI:SS'";
            }

            paramMap.put("format", "'YYYY-MM-DD" + end);

            list = this.utisMapper.getPeriodRcvMoctTraffStatistics(paramMap);
        }
        Workbook workbook = makeExcel(sheetName, headers, list.size());
        Sheet sheet = workbook.getSheet(sheetName);

        long[] totals = new long[headers.length - 1];
        for (SndMolitTraffVo vo : list) {
            Row row = sheet.getRow(rowNum++);
            setCellValues(row,vo.getLogDate(), vo.getRcvCnt());
            totals[0] += vo.getRcvCnt();
        }
        Row totalRow = sheet.getRow(rowNum);
        if (totalRow == null) totalRow = sheet.createRow(rowNum);
        setCellValues(totalRow, "합계", totals[0]);
        return workbook;
    }

    /**
     * 지역센터 신호연계 통신이력
     * @param paramMap Parameter
     * @param headers Excel Header
     * @param sheetName Excel Sheet Name
     * @return Workbook
     */
    public Workbook getSigCommWorkBook(Map<String, Object> paramMap, String[] headers, String sheetName) {
        int rowNum = 1;
        List<SigCommStatisticsVo> list = this.utisMapper.getPeriodSigCommStatistics(paramMap);
        Workbook workbook = makeExcel(sheetName, headers, list.size());
        Sheet sheet = workbook.getSheet(sheetName);

        for (SigCommStatisticsVo vo : list) {
            Row row = sheet.getRow(rowNum++);
            setCellValues(row, vo.getColdate(), vo.getCollctSn(), vo.getCommCd(), vo.getComNm());
        }
        Row totalRow = sheet.getRow(rowNum);
        if (totalRow == null) totalRow = sheet.createRow(rowNum);
        setCellValues(totalRow, "합계", list.size(), "", "");
        return workbook;
    }
}


