package com.pandoli365.bibimbap.controller;

import com.pandoli365.bibimbap.data.GameData;
import com.pandoli365.bibimbap.data.JamData;
import com.pandoli365.bibimbap.data.JamEntryData;
import com.pandoli365.bibimbap.data.JamTeamData;
import com.pandoli365.bibimbap.mapper.GamesMapper;
import com.pandoli365.bibimbap.mapper.JamAwardsMapper;
import com.pandoli365.bibimbap.mapper.JamEntriesMapper;
import com.pandoli365.bibimbap.mapper.JamTeamMembersMapper;
import com.pandoli365.bibimbap.mapper.JamTeamsMapper;
import com.pandoli365.bibimbap.mapper.JamsMapper;
import com.pandoli365.bibimbap.security.CsrfTokens;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
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 java.time.OffsetDateTime;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Controller
public class JamController {

    private static final int PAGE_SIZE = 20;

    private final JamsMapper jamsMapper;
    private final JamEntriesMapper jamEntriesMapper;
    private final JamTeamsMapper jamTeamsMapper;
    private final JamTeamMembersMapper jamTeamMembersMapper;
    private final GamesMapper gamesMapper;
    private final JamAwardsMapper jamAwardsMapper;

    public JamController(
            JamsMapper jamsMapper,
            JamEntriesMapper jamEntriesMapper,
            JamTeamsMapper jamTeamsMapper,
            JamTeamMembersMapper jamTeamMembersMapper,
            GamesMapper gamesMapper,
            JamAwardsMapper jamAwardsMapper
    ) {
        this.jamsMapper = jamsMapper;
        this.jamEntriesMapper = jamEntriesMapper;
        this.jamTeamsMapper = jamTeamsMapper;
        this.jamTeamMembersMapper = jamTeamMembersMapper;
        this.gamesMapper = gamesMapper;
        this.jamAwardsMapper = jamAwardsMapper;
    }

    @GetMapping("/jams")
    public String list(@RequestParam(name = "cursor", required = false) String cursor, Model model) {
        OffsetDateTime cursorCreatedAt = null;
        Long cursorId = null;
        if (cursor != null && !cursor.isBlank()) {
            int separator = cursor.lastIndexOf('_');
            if (separator > 0 && separator < cursor.length() - 1) {
                try {
                    cursorCreatedAt = OffsetDateTime.parse(cursor.substring(0, separator));
                    cursorId = Long.parseLong(cursor.substring(separator + 1));
                } catch (RuntimeException e) {
                    cursorCreatedAt = null;
                    cursorId = null;
                }
            }
        }

        List<JamData> jams = jamsMapper.listVisibleKeyset(cursorCreatedAt, cursorId, PAGE_SIZE + 1);
        boolean hasNext = jams.size() > PAGE_SIZE;
        if (hasNext) {
            jams = jams.subList(0, PAGE_SIZE);
        }
        String nextCursor = null;
        if (hasNext) {
            JamData lastJam = jams.get(jams.size() - 1);
            nextCursor = lastJam.getCreatedAt().toString() + "_" + lastJam.getId();
        }

        model.addAttribute("jams", jams);
        model.addAttribute("nextCursor", nextCursor);
        return "jam-list";
    }

    @GetMapping("/jams/{slug}")
    public String detail(@PathVariable("slug") String slug, Model model, HttpServletRequest request) {
        JamData jam = jamsMapper.getBySlug(slug);
        if (jam == null || Boolean.FALSE.equals(jam.getIsVisible())) {
            return "redirect:/jams";
        }
        model.addAttribute("jam", jam);
        model.addAttribute("entries", jamEntriesMapper.listByJam(jam.getId()));
        model.addAttribute("teams", jamTeamsMapper.listByJam(jam.getId()));
        model.addAttribute("awardsSummary", jamAwardsMapper.listByJamWithGame(jam.getId()));
        model.addAttribute("csrfToken", CsrfTokens.getOrCreate(request.getSession()));
        return "jam-detail";
    }

    @PostMapping("/jams/{slug}/entries")
    @Transactional
    public ResponseEntity<Map<String, Object>> submitEntry(
            @PathVariable("slug") String slug,
            @RequestParam("gameId") long gameId,
            @RequestParam(name = "jamTeamId", required = false) Long jamTeamId,
            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, "로그인이 필요합니다.");
        }
        JamData jam = jamsMapper.getBySlug(slug);
        if (jam == null) {
            return response(HttpStatus.NOT_FOUND, "잼을 찾을 수 없습니다.");
        }
        if (!"RECRUIT".equals(jam.getStatus()) && !"DEV".equals(jam.getStatus())) {
            return response(HttpStatus.UNPROCESSABLE_ENTITY, "지금은 출품할 수 없는 상태입니다.");
        }
        GameData game = gamesMapper.getGame(gameId);
        if (game == null) {
            return response(HttpStatus.NOT_FOUND, "게임을 찾을 수 없습니다.");
        }
        if (jamEntriesMapper.exists(jam.getId(), gameId)) {
            return response(HttpStatus.CONFLICT, "이미 출품된 게임입니다.");
        }

        String entrantType;
        Long entrantUserId;
        if (jamTeamId == null) {
            entrantType = "USER";
            if (!userId.equals(game.getUserId())) {
                return response(HttpStatus.UNPROCESSABLE_ENTITY, "본인 게임만 출품할 수 있습니다.");
            }
            entrantUserId = userId;
        } else {
            entrantType = "TEAM";
            if (!jamTeamMembersMapper.exists(jamTeamId, userId)) {
                return response(HttpStatus.UNPROCESSABLE_ENTITY, "팀 멤버만 출품할 수 있습니다.");
            }
            entrantUserId = null;
        }

        JamEntryData entry = new JamEntryData();
        entry.setJamId(jam.getId());
        entry.setGameId(gameId);
        entry.setEntrantType(entrantType);
        entry.setEntrantUserId(entrantUserId);
        entry.setJamTeamId(jamTeamId);
        try {
            jamEntriesMapper.insert(entry);
        } catch (DuplicateKeyException e) {
            return response(HttpStatus.CONFLICT, "이미 출품된 게임입니다.");
        }

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

    @PostMapping("/jams/{slug}/teams")
    @Transactional
    public ResponseEntity<Map<String, Object>> submitTeam(
            @PathVariable("slug") String slug,
            @RequestParam("name") String name,
            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, "로그인이 필요합니다.");
        }
        JamData jam = jamsMapper.getBySlug(slug);
        if (jam == null) {
            return response(HttpStatus.NOT_FOUND, "잼을 찾을 수 없습니다.");
        }
        if (!"RECRUIT".equals(jam.getStatus()) && !"DEV".equals(jam.getStatus())) {
            return response(HttpStatus.UNPROCESSABLE_ENTITY, "지금은 출품할 수 없는 상태입니다.");
        }
        String normalizedName = trimToNull(name);
        if (normalizedName == null || normalizedName.length() > 120) {
            return response(HttpStatus.UNPROCESSABLE_ENTITY, "팀 이름을 120자 이내로 입력해 주세요.");
        }

        JamTeamData team = new JamTeamData();
        team.setJamId(jam.getId());
        team.setName(normalizedName);
        team.setOwnerUserId(userId);
        jamTeamsMapper.insert(team);

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

    @PostMapping("/jams/{slug}/teams/{teamId}/members")
    @Transactional
    public ResponseEntity<Map<String, Object>> addMember(
            @PathVariable("slug") String slug,
            @PathVariable("teamId") long teamId,
            @RequestParam(name = "userId") long memberUserId,
            HttpServletRequest request,
            HttpSession session
    ) {
        if (!CsrfTokens.isValid(request)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body(CsrfTokens.errorBody());
        }
        Long actorId = sessionUserId(session);
        if (actorId == null) {
            return response(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");
        }
        JamData jam = jamsMapper.getBySlug(slug);
        if (jam == null) {
            return response(HttpStatus.NOT_FOUND, "잼을 찾을 수 없습니다.");
        }
        JamTeamData team = jamTeamsMapper.getById(teamId);
        if (team == null) {
            return response(HttpStatus.NOT_FOUND, "팀을 찾을 수 없습니다.");
        }
        if (!actorId.equals(team.getOwnerUserId())) {
            return response(HttpStatus.UNPROCESSABLE_ENTITY, "팀장만 멤버를 추가할 수 있습니다.");
        }
        if (jamTeamMembersMapper.exists(teamId, memberUserId)) {
            return response(HttpStatus.UNPROCESSABLE_ENTITY, "이미 팀 멤버입니다.");
        }
        try {
            jamTeamMembersMapper.insert(teamId, memberUserId);
        } catch (DuplicateKeyException e) {
            return response(HttpStatus.UNPROCESSABLE_ENTITY, "이미 팀 멤버입니다.");
        }
        return response(HttpStatus.OK, "팀 멤버가 추가되었습니다.");
    }

    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);
    }
}
