package com.pandoli365.bibimbap.controller;

import com.pandoli365.bibimbap.data.GameData;
import com.pandoli365.bibimbap.data.TagData;
import com.pandoli365.bibimbap.mapper.GameTagsMapper;
import com.pandoli365.bibimbap.mapper.GamesMapper;
import com.pandoli365.bibimbap.mapper.TagsMapper;
import com.pandoli365.bibimbap.security.CsrfTokens;
import com.pandoli365.bibimbap.security.PermissionGate;
import com.pandoli365.bibimbap.security.PermissionKeys;
import com.pandoli365.bibimbap.util.TagSanitizer;
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.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

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

@Controller
public class TagController {

    private static final Set<String> TAG_TYPES = Set.of("GAME", "JAM", "COMMON");

    private final TagsMapper tagsMapper;
    private final GameTagsMapper gameTagsMapper;
    private final GamesMapper gamesMapper;
    private final PermissionGate permissionGate;

    public TagController(TagsMapper tagsMapper,
                         GameTagsMapper gameTagsMapper,
                         GamesMapper gamesMapper,
                         PermissionGate permissionGate) {
        this.tagsMapper = tagsMapper;
        this.gameTagsMapper = gameTagsMapper;
        this.gamesMapper = gamesMapper;
        this.permissionGate = permissionGate;
    }

    @PostMapping("/tags")
    @Transactional
    public ResponseEntity<Map<String, Object>> createUserTag(
            @RequestParam(name = "name", required = false) String name,
            @RequestParam(name = "tagType", required = false) String tagType,
            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, "로그인이 필요합니다.");
        }
        String normalizedType = trimToNull(tagType);
        if (!isValidTagType(normalizedType)) {
            return response(HttpStatus.BAD_REQUEST, "태그 유형을 확인해 주세요.");
        }
        String slug = TagSanitizer.toSlug(name);
        if (!TagSanitizer.isAllowed(slug)) {
            return response(HttpStatus.BAD_REQUEST, "태그 이름을 확인해 주세요.");
        }

        TagData existing = tagsMapper.findBySlug(slug);
        if (existing != null) {
            return tagBody(existing.getId(), existing.getName(), existing.getSlug(), existing.getIsActive());
        }

        TagData tag = new TagData();
        tag.setName(name.trim());
        tag.setSlug(slug);
        tag.setTagType(normalizedType);
        tag.setIsActive(false);
        tag.setCreatedBy(userId);
        tagsMapper.insertTag(tag);

        return tagBody(tag.getId(), tag.getName(), tag.getSlug(), false);
    }

    @PostMapping("/admin/tags")
    @Transactional
    public ResponseEntity<Map<String, Object>> createAdminTag(
            @RequestParam(name = "name", required = false) String name,
            @RequestParam(name = "tagType", required = false) String tagType,
            HttpServletRequest request,
            HttpSession session
    ) {
        if (!CsrfTokens.isValid(request)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body(CsrfTokens.errorBody());
        }
        if (!permissionGate.has(session, PermissionKeys.CONTENT_MODERATE.name())) {
            return response(HttpStatus.FORBIDDEN, "권한이 없습니다.");
        }
        Long userId = sessionUserId(session);
        String normalizedType = trimToNull(tagType);
        if (!isValidTagType(normalizedType)) {
            return response(HttpStatus.BAD_REQUEST, "태그 유형을 확인해 주세요.");
        }
        String slug = TagSanitizer.toSlug(name);
        if (!TagSanitizer.isAllowed(slug)) {
            return response(HttpStatus.BAD_REQUEST, "태그 이름을 확인해 주세요.");
        }

        TagData existing = tagsMapper.findBySlug(slug);
        if (existing != null) {
            return tagBody(existing.getId(), existing.getName(), existing.getSlug(), existing.getIsActive());
        }

        TagData tag = new TagData();
        tag.setName(name.trim());
        tag.setSlug(slug);
        tag.setTagType(normalizedType);
        tag.setIsActive(true);
        tag.setCreatedBy(userId);
        tagsMapper.insertTag(tag);

        return tagBody(tag.getId(), tag.getName(), tag.getSlug(), true);
    }

    @PostMapping("/admin/tags/{id}/approve")
    @Transactional
    public ResponseEntity<Map<String, Object>> approveTag(
            @PathVariable("id") long id,
            HttpServletRequest request,
            HttpSession session
    ) {
        if (!CsrfTokens.isValid(request)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body(CsrfTokens.errorBody());
        }
        if (!permissionGate.has(session, PermissionKeys.CONTENT_MODERATE.name())) {
            return response(HttpStatus.FORBIDDEN, "권한이 없습니다.");
        }
        tagsMapper.approve(id);

        Map<String, Object> body = new LinkedHashMap<>();
        body.put("id", id);
        body.put("isActive", true);
        return ResponseEntity.ok(body);
    }

    @PostMapping("/admin/tags/{id}/deactivate")
    @Transactional
    public ResponseEntity<Map<String, Object>> deactivateTag(
            @PathVariable("id") long id,
            HttpServletRequest request,
            HttpSession session
    ) {
        if (!CsrfTokens.isValid(request)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body(CsrfTokens.errorBody());
        }
        if (!permissionGate.has(session, PermissionKeys.CONTENT_MODERATE.name())) {
            return response(HttpStatus.FORBIDDEN, "권한이 없습니다.");
        }
        tagsMapper.deactivate(id);

        Map<String, Object> body = new LinkedHashMap<>();
        body.put("id", id);
        body.put("isActive", false);
        return ResponseEntity.ok(body);
    }

    @PostMapping("/games/{gameId}/tags")
    @Transactional
    public ResponseEntity<Map<String, Object>> attachGameTags(
            @PathVariable("gameId") long gameId,
            @RequestParam(name = "tagIds", required = false) List<Long> tagIds,
            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 (tagIds == null || tagIds.isEmpty()) {
            return response(HttpStatus.BAD_REQUEST, "부착할 태그를 선택해 주세요.");
        }

        GameData game = gamesMapper.getGame(gameId);
        if (game == null) {
            return response(HttpStatus.NOT_FOUND, "게임을 찾을 수 없습니다.");
        }
        boolean owner = userId.equals(game.getUserId());
        boolean moderator = permissionGate.has(session, PermissionKeys.CONTENT_MODERATE.name());
        if (!owner && !moderator) {
            return response(HttpStatus.FORBIDDEN, "게임 소유자 또는 모더레이터만 태그를 부착할 수 있습니다.");
        }

        gameTagsMapper.attachTags(gameId, tagIds);

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

    @GetMapping("/tags")
    @ResponseBody
    public List<Map<String, Object>> listTags(
            @RequestParam(name = "type", required = false) String type,
            @RequestParam(name = "q", required = false) String q
    ) {
        List<TagData> tags = tagsMapper.listActiveByType(trimToNull(type), trimToNull(q));
        List<Map<String, Object>> result = new ArrayList<>();
        for (TagData tag : tags) {
            Map<String, Object> item = new LinkedHashMap<>();
            item.put("id", tag.getId());
            item.put("name", tag.getName());
            item.put("slug", tag.getSlug());
            result.add(item);
        }
        return result;
    }

    private ResponseEntity<Map<String, Object>> tagBody(Long id, String name, String slug, Boolean isActive) {
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("id", id);
        body.put("name", name);
        body.put("slug", slug);
        body.put("isActive", isActive);
        return ResponseEntity.ok(body);
    }

    private boolean isValidTagType(String tagType) {
        return tagType != null && TAG_TYPES.contains(tagType);
    }

    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 trimToNull(String value) {
        if (value == null) {
            return null;
        }
        String text = value.trim();
        return text.isBlank() ? 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);
    }
}
