package com.pandoli365.bibimbap.controller.api;

import com.pandoli365.bibimbap.data.GameCommentData;
import com.pandoli365.bibimbap.mapper.GameCommentsMapper;
import com.pandoli365.bibimbap.mapper.GamesMapper;
import com.pandoli365.bibimbap.security.CsrfTokens;
import com.pandoli365.bibimbap.security.PermissionGate;
import com.pandoli365.bibimbap.util.TextNormalizer;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Controller
public class GameCommentController {

    private static final int CONTENT_MAX = 200;
    private static final int PAGE_SIZE = 20;

    private final GameCommentsMapper gameCommentsMapper;
    private final GamesMapper gamesMapper;
    private final PermissionGate permissionGate;

    public GameCommentController(GameCommentsMapper gameCommentsMapper, GamesMapper gamesMapper, PermissionGate permissionGate) {
        this.gameCommentsMapper = gameCommentsMapper;
        this.gamesMapper = gamesMapper;
        this.permissionGate = permissionGate;
    }

    @GetMapping("/game/{id}/comments")
    public ResponseEntity<Map<String, Object>> listComments(
            @PathVariable("id") long id,
            @RequestParam(name = "page", defaultValue = "0") int page,
            @RequestParam(name = "sort", required = false) String sort
    ) {
        if (gamesMapper.getGame(id) == null) {
            return response(HttpStatus.NOT_FOUND, "게임을 찾을 수 없습니다.");
        }

        String sortEnum = normalizeCommentSort(sort);
        int offset = Math.max(page, 0) * PAGE_SIZE;
        List<GameCommentData> rows = gameCommentsMapper.listGameComments(id, sortEnum, offset, PAGE_SIZE + 1);
        boolean hasMore = rows.size() > PAGE_SIZE;
        int count = Math.min(rows.size(), PAGE_SIZE);

        List<Map<String, Object>> comments = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            comments.add(commentView(rows.get(i)));
        }

        Map<String, Object> body = new LinkedHashMap<>();
        body.put("status", 200);
        body.put("comments", comments);
        body.put("hasMore", hasMore);
        return ResponseEntity.ok(body);
    }

    @PostMapping("/game/{id}/comments")
    @Transactional
    public ResponseEntity<Map<String, Object>> createComment(
            @PathVariable("id") long id,
            @RequestParam(name = "content", required = false) String content,
            HttpServletRequest request,
            HttpSession session
    ) {
        if (!CsrfTokens.isValid(request)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body(CsrfTokens.errorBody());
        }
        Long userId = sessionUserId(session);
        if (userId == null) {
            return response(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");
        }
        if (gamesMapper.getGame(id) == null) {
            return response(HttpStatus.NOT_FOUND, "게임을 찾을 수 없습니다.");
        }

        String normalizedContent = trimToNull(TextNormalizer.normalize(content));
        if (normalizedContent == null || normalizedContent.length() > CONTENT_MAX) {
            return response(HttpStatus.BAD_REQUEST, "덧글은 200자 이내로 입력해 주세요.");
        }

        String authorName = trimToEmpty(sessionDisplayName(session));
        GameCommentData comment = new GameCommentData();
        comment.setGameId(id);
        comment.setUserId(userId);
        comment.setNickname(authorName);
        comment.setContent(normalizedContent);
        gameCommentsMapper.addGameComment(comment);
        if (comment.getId() == null) {
            return response(HttpStatus.INTERNAL_SERVER_ERROR, "덧글 등록 결과를 확인하지 못했습니다.");
        }

        GameCommentData created = gameCommentsMapper.getGameComment(comment.getId());
        Map<String, Object> result = created != null ? commentView(created) : commentView(comment);
        result.put("status", 200);
        result.put("message", "덧글이 등록되었습니다.");
        return ResponseEntity.ok(result);
    }

    @PutMapping("/game/{id}/comments/{commentId}")
    @Transactional
    public ResponseEntity<Map<String, Object>> updateComment(
            @PathVariable("id") long id,
            @PathVariable("commentId") long commentId,
            @RequestParam(name = "content", required = false) String content,
            HttpServletRequest request,
            HttpSession session
    ) {
        if (!CsrfTokens.isValid(request)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body(CsrfTokens.errorBody());
        }
        Long userId = sessionUserId(session);
        if (userId == null) {
            return response(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");
        }

        GameCommentData comment = gameCommentsMapper.getGameComment(commentId);
        if (comment == null || !Long.valueOf(id).equals(comment.getGameId())) {
            return response(HttpStatus.NOT_FOUND, "덧글을 찾을 수 없습니다.");
        }
        if (!canModify(userId, comment.getUserId(), session)) {
            return response(HttpStatus.FORBIDDEN, "작성자만 수정할 수 있습니다.");
        }

        String normalizedContent = trimToNull(TextNormalizer.normalize(content));
        if (normalizedContent == null || normalizedContent.length() > CONTENT_MAX) {
            return response(HttpStatus.BAD_REQUEST, "덧글은 200자 이내로 입력해 주세요.");
        }

        comment.setContent(normalizedContent);
        gameCommentsMapper.editGameComment(comment);

        GameCommentData updated = gameCommentsMapper.getGameComment(commentId);
        Map<String, Object> result = updated != null ? commentView(updated) : commentView(comment);
        result.put("status", 200);
        result.put("message", "덧글이 수정되었습니다.");
        return ResponseEntity.ok(result);
    }

    @DeleteMapping("/game/{id}/comments/{commentId}")
    @Transactional
    public ResponseEntity<Map<String, Object>> deleteComment(
            @PathVariable("id") long id,
            @PathVariable("commentId") long commentId,
            HttpServletRequest request,
            HttpSession session
    ) {
        if (!CsrfTokens.isValid(request)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body(CsrfTokens.errorBody());
        }
        Long userId = sessionUserId(session);
        if (userId == null) {
            return response(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");
        }

        GameCommentData comment = gameCommentsMapper.getGameComment(commentId);
        if (comment == null || !Long.valueOf(id).equals(comment.getGameId())) {
            return response(HttpStatus.NOT_FOUND, "덧글을 찾을 수 없습니다.");
        }
        if (!canModify(userId, comment.getUserId(), session)) {
            return response(HttpStatus.FORBIDDEN, "작성자만 삭제할 수 있습니다.");
        }

        gameCommentsMapper.softDeleteGameComment(commentId);

        Map<String, Object> body = new LinkedHashMap<>();
        body.put("status", 200);
        body.put("message", "덧글이 삭제되었습니다.");
        return ResponseEntity.ok(body);
    }

    private Map<String, Object> commentView(GameCommentData comment) {
        Map<String, Object> view = new LinkedHashMap<>();
        view.put("commentId", comment.getId());
        view.put("gameId", comment.getGameId());
        view.put("authorName", comment.getAuthorName() != null ? comment.getAuthorName() : comment.getNickname());
        view.put("userId", comment.getUserId());
        view.put("content", comment.getContent());
        view.put("createdAt", comment.getCreatedAt());
        view.put("edited", comment.getEdited() != null && comment.getEdited());
        view.put("updatedAt", comment.getUpdatedAt());
        return view;
    }

    private String normalizeCommentSort(String sort) {
        return "newest".equals(sort) ? "newest" : "oldest";
    }

    private boolean canModify(Long currentUserId, Long authorUserId, HttpSession session) {
        return (authorUserId != null && authorUserId.equals(currentUserId)) || permissionGate.canModerate(session);
    }

    private Long sessionUserId(HttpSession session) {
        if (session == null) {
            return null;
        }
        Object userId = session.getAttribute("userId");
        if (userId instanceof Number number) {
            return number.longValue();
        }
        if (userId instanceof String text) {
            try {
                return Long.parseLong(text);
            } catch (NumberFormatException e) {
                return null;
            }
        }
        return null;
    }

    private String sessionDisplayName(HttpSession session) {
        if (session == null) {
            return null;
        }
        Object displayName = session.getAttribute("displayName");
        return displayName instanceof String text ? text : null;
    }

    private String trimToNull(String value) {
        if (value == null) {
            return null;
        }
        String text = value.trim();
        return text.isBlank() ? null : text;
    }

    private String trimToEmpty(String value) {
        String text = trimToNull(value);
        return text == null ? "" : text;
    }

    private ResponseEntity<Map<String, Object>> response(HttpStatus status, String message) {
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("status", status.value());
        body.put("message", message);
        return ResponseEntity.status(status).body(body);
    }
}
