package com.kospo.talk.controller;

import com.kospo.talk.config.exception.CustomErrorResponse;
import com.kospo.talk.config.exception.ErrorCode;
import com.kospo.talk.config.utils.CommonUtils;
import com.kospo.talk.exception.CustomException;
import com.kospo.talk.model.*;
import com.kospo.talk.payload.ExternalMessageDto;
import com.kospo.talk.payload.MessageDto;
import com.kospo.talk.payload.TalkDto;
import com.kospo.talk.payload.TalkReadDto;
import com.kospo.talk.service.TalkAttachService;
import com.kospo.talk.service.TalkMessageService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.core.io.InputStreamResource;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import org.yaml.snakeyaml.util.UriEncoder;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.Principal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@Tag(name = "Talk Message Api", description = "톡 대화내용 관리")
@RequestMapping("/api")
@RequiredArgsConstructor
public class TalkMessageController {
    private final TalkMessageService talkMessageService;
    private final TalkAttachService talkAttachService;

    @Operation(summary = "대화 내용 조회", description = "채팅 아이디로 대화 내용을 조회(날짜별 조회) 한다.")
    @ApiResponses({
            @ApiResponse(description = "Success", responseCode = "200",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", array = @ArraySchema(
                            schema = @Schema(oneOf =
                                    {TalkDto.class})
                    ))),
            @ApiResponse(description = "Not found", responseCode = "404",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "text/plain", schema = @Schema(oneOf =
                            {String.class}))),
            @ApiResponse(description = "Internal Error", responseCode = "500",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", schema = @Schema(oneOf =
                            {CustomErrorResponse.class})))
    })
    @GetMapping("/message")
    public ResponseEntity contentList(
            @Parameter(description = "룸아이디") @RequestParam(name = "talkId") String talkId,
            @Parameter(description = "날짜") @RequestParam(name = "insDate") String insDate,
            Principal principal) {
        return ResponseEntity.ok(
                talkMessageService.getMessage(talkId, insDate)
        );
    }


    @Operation(summary = "대화 내용 조회(30건)", description = "사번으로 본인이 참여된 톡방 데이터 호출")
    @ApiResponses({
            @ApiResponse(description = "Success", responseCode = "200",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", array = @ArraySchema(
                            schema = @Schema(oneOf =
                                    {TalkDto.class})
                    ))),
            @ApiResponse(description = "Not found", responseCode = "404",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "text/plain", schema = @Schema(oneOf =
                            {String.class}))),
            @ApiResponse(description = "Internal Error", responseCode = "500",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", schema = @Schema(oneOf =
                            {CustomErrorResponse.class})))
    })
    @GetMapping("/message/all")
    public ResponseEntity getMessageAll(
            Principal principal) {
        return ResponseEntity.ok(
                talkMessageService.getMessageAll(principal.getName())
        );
    }


    @Operation(summary = "(소켓 재연결)추가된 대화 내용 조회", description = "마지막 등록시간(last_message_date) 보다 최신 데이터 조회")
    @ApiResponses({
            @ApiResponse(description = "Success", responseCode = "200",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", array = @ArraySchema(
                            schema = @Schema(oneOf =
                                    {TalkDto.class})
                    ))),
            @ApiResponse(description = "Not found", responseCode = "404",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "text/plain", schema = @Schema(oneOf =
                            {String.class}))),
            @ApiResponse(description = "Internal Error", responseCode = "500",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", schema = @Schema(oneOf =
                            {CustomErrorResponse.class})))
    })
    @GetMapping("/message/more/all")
    public ResponseEntity getMessageAllByInsDate(
            @Parameter(description = "마지막 메시지 시간") @RequestParam(name = "insDate") String insDate,
            Principal principal) {
        return ResponseEntity.ok(
                talkMessageService.getMessageAllMore(principal.getName(), UriEncoder.decode(insDate))
        );
    }


    @Operation(summary = "대화 내용 조회", description = "채팅 아이디로 대화 내용을 조회(라인 조회) 한다.")
    @ApiResponses({
            @ApiResponse(description = "Success", responseCode = "200",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", array = @ArraySchema(
                            schema = @Schema(oneOf =
                                    {TalkDto.class})
                    ))),
            @ApiResponse(description = "Not found", responseCode = "404",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "text/plain", schema = @Schema(oneOf =
                            {String.class}))),
            @ApiResponse(description = "Internal Error", responseCode = "500",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", schema = @Schema(oneOf =
                            {CustomErrorResponse.class})))
    })
    @GetMapping("/message/line")
    public ResponseEntity contentLine(
            @Parameter(description = "룸아이디") @RequestParam(name = "talkId") String talkId,
            @Parameter(description = "시작 라인") @RequestParam(name = "start") Integer start,
            @Parameter(description = "가져올 데이터수") @RequestParam(name = "row") Integer row,
            @Parameter(description = "마지막 메시지 일자") @RequestParam(name = "insDate", required = false, defaultValue = "") String insDate,
            Principal principal) {
        return ResponseEntity.ok(
                talkMessageService.getMessageLine(talkId, start, row, insDate)
        );
    }

    @Operation(summary = "대화 내용 조회", description = "채팅 아이디로 대화 내용을 조회(검색 조회) 한다.")
    @ApiResponses({
            @ApiResponse(description = "Success", responseCode = "200",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", array = @ArraySchema(
                            schema = @Schema(oneOf =
                                    {TalkDto.class})
                    ))),
            @ApiResponse(description = "Not found", responseCode = "404",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "text/plain", schema = @Schema(oneOf =
                            {String.class}))),
            @ApiResponse(description = "Internal Error", responseCode = "500",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", schema = @Schema(oneOf =
                            {CustomErrorResponse.class})))
    })
    @GetMapping("/message/search")
    public ResponseEntity contentSearchList(
            @Parameter(description = "룸아이디") @RequestParam(name = "talkId") String talkId,
            @Parameter(description = "검색") @RequestParam(name = "search") String search,
            Principal principal) {
        return ResponseEntity.ok(
                talkMessageService.getSearchMessage(talkId, search)
        );
    }

    @Operation(summary = "전체 검색", description = "특정 업무의 모든 채팅방의 내용을 검색한다.")
    @ApiResponses({
            @ApiResponse(description = "Success", responseCode = "200",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", array = @ArraySchema(
                            schema = @Schema(oneOf =
                                    {TalkDto.class})
                    ))),
            @ApiResponse(description = "Not found", responseCode = "404",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "text/plain", schema = @Schema(oneOf =
                            {String.class}))),
            @ApiResponse(description = "Internal Error", responseCode = "500",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", schema = @Schema(oneOf =
                            {CustomErrorResponse.class})))
    })
    @GetMapping("/message/search/all")
    public ResponseEntity talkListSearchContent(
            @Parameter(description = "검색") @RequestParam(name = "search") String search,
            Principal principal) {
        return ResponseEntity.ok(
                talkMessageService.getAllSearchContent(search, principal.getName())
        );
    }


    @Operation(summary = "대화 읽음 처리", description = "채팅 아이디로 대화 내용을 (상대방 메시지) 전체 읽음 처리 한다.")
    @ApiResponses({
            @ApiResponse(description = "Success", responseCode = "200",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", schema = @Schema(oneOf =
                            {Integer.class})
                    )),
            @ApiResponse(description = "Not found", responseCode = "404",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "text/plain", schema = @Schema(oneOf =
                            {String.class}))),
            @ApiResponse(description = "Internal Error", responseCode = "500",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", schema = @Schema(oneOf =
                            {CustomErrorResponse.class})))
    })
    @PostMapping("/message/read")
    public ResponseEntity contentRead(
            @RequestBody TalkReadDto talkReadDto,
            Principal principal) {

        return ResponseEntity.ok(
                talkMessageService.read(talkReadDto.getTalkId(), principal.getName())
        );
    }


    @Operation(summary = "첨부파일 썸네일", description = "첨부파일 썸네일 이이지 사이즈 조절 및 crop 기능")
    @ApiResponses({
            @ApiResponse(description = "Success", responseCode = "200",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", schema = @Schema(oneOf =
                            {Byte[].class})
                    )),
            @ApiResponse(description = "Not found", responseCode = "404",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "text/plain", schema = @Schema(oneOf =
                            {String.class}))),
            @ApiResponse(description = "Internal Error", responseCode = "500",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", schema = @Schema(oneOf =
                            {CustomErrorResponse.class})))
    })
    @GetMapping("/message/thumb")
    public ResponseEntity<byte[]> contentThumbRead(
            @Parameter(description = "채팅 아이디") @RequestParam("talkId") String talkId,
            @Parameter(description = "사번") @RequestParam("sabun") String sabun,
            @Parameter(description = "등록일자") @RequestParam("insDate") String insDate,
            @Parameter(description = "정렬") @RequestParam("sort") Integer sort,
            @Parameter(description = "size") @RequestParam("size") Integer size,
            Principal principal) {

        byte[] thumbnailBytes = talkAttachService.readThumb(
                TalkAttachId.builder()
                        .talkId(talkId)
                        .sabun(sabun)
                        .insDate(CommonUtils.stringToDate(insDate, "yyyy-MM-dd HH:mm:ss.SSSSSS"))
                        .sort(sort).build(), size
        );

        // HTTP 헤더 설정
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "max-age=31536000, public");
        headers.setContentType(MediaType.IMAGE_PNG);
        headers.setContentLength(thumbnailBytes.length);

        return new ResponseEntity<>(thumbnailBytes, headers, HttpStatus.OK);
    }

    @Operation(summary = "첨부파일 썸네일", description = "첨부파일 썸네일 이이지 사이즈 조절 및 crop 기능")
    @ApiResponses({
            @ApiResponse(description = "Success", responseCode = "200",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", schema = @Schema(oneOf =
                            {Byte[].class})
                    )),
            @ApiResponse(description = "Not found", responseCode = "404",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "text/plain", schema = @Schema(oneOf =
                            {String.class}))),
            @ApiResponse(description = "Internal Error", responseCode = "500",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", schema = @Schema(oneOf =
                            {CustomErrorResponse.class})))
    })
    @GetMapping("/message/image")
    public ResponseEntity<byte[]> contentImageRead(
            @Parameter(description = "채팅 아이디") @RequestParam("talkId") String talkId,
            @Parameter(description = "사번") @RequestParam("sabun") String sabun,
            @Parameter(description = "등록일자") @RequestParam("insDate") String insDate,
            @Parameter(description = "정렬") @RequestParam("sort") Integer sort,
            Principal principal) {

        byte[] thumbnailBytes = talkAttachService.readFile(
                TalkAttachId.builder()
                        .talkId(talkId)
                        .sabun(sabun)
                        .insDate(CommonUtils.stringToDate(insDate, "yyyy-MM-dd HH:mm:ss.SSSSSS"))
                        .sort(sort).build()
        );

        // HTTP 헤더 설정
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.IMAGE_PNG);
        headers.set("Cache-Control", "max-age=31536000, public");
        headers.setContentLength(thumbnailBytes.length);

        return new ResponseEntity<>(thumbnailBytes, headers, HttpStatus.OK);
    }

    @Operation(summary = "첨부파일 다운로드", description = "이미지 다운로드")
    @ApiResponses({
            @ApiResponse(description = "Success", responseCode = "200",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", schema = @Schema(oneOf =
                            {Byte[].class})
                    )),
            @ApiResponse(description = "Not found", responseCode = "404",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "text/plain", schema = @Schema(oneOf =
                            {String.class}))),
            @ApiResponse(description = "Internal Error", responseCode = "500",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", schema = @Schema(oneOf =
                            {CustomErrorResponse.class})))
    })
    @GetMapping("/message/download")
    public ResponseEntity<InputStreamResource> contentFileDownload(
            @Parameter(description = "채팅 아이디") @RequestParam("talkId") String talkId,
            @Parameter(description = "사번") @RequestParam("sabun") String sabun,
            @Parameter(description = "등록일자") @RequestParam("insDate") String insDate,
            Principal principal) throws IOException {

        ByteArrayInputStream bais = null;
        List<TalkAttach> lists = talkAttachService.getTalkAttachList(talkId, sabun, insDate);
        HttpHeaders headers = new HttpHeaders();

        if (lists.size() == 1) {
            // HTTP 헤더 설정
            headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + UriEncoder.encode(lists.get(0).getOnm()));
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            bais = talkAttachService.getTalkAttachByte(lists.get(0));
        } else if (lists.isEmpty()) {
            throw new CustomException(ErrorCode.NO_RESULT);
        } else {
            // HTTP 헤더 설정
            headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + UriEncoder.encode(lists.get(0).getOnm() + " 외 " + (lists.size() - 1) + ".zip"));
            headers.setContentType(MediaType.parseMediaType("application/zip"));
            bais = talkAttachService.getTalkAttachCompression(lists);
        }

        return new ResponseEntity<>(new InputStreamResource(bais), headers, HttpStatus.OK);
    }

    @Operation(summary = "외부 메시지 저장", description = "외부 메시지 저장")
    @ApiResponses({
            @ApiResponse(description = "Success", responseCode = "200",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", schema = @Schema(oneOf =
                            {String.class})
                    )),
            @ApiResponse(description = "Not found", responseCode = "404",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "text/plain", schema = @Schema(oneOf =
                            {String.class}))),
            @ApiResponse(description = "Internal Error", responseCode = "500",
                    content = @io.swagger.v3.oas.annotations.media.Content(mediaType = "application/json", schema = @Schema(oneOf =
                            {CustomErrorResponse.class})))
    })
    @PostMapping("/message")
    public ResponseEntity externalSave(@RequestBody ExternalMessageDto externalMessageDto) {
        talkMessageService.externalSave(externalMessageDto);
        return ResponseEntity.ok("s");
    }
}
