package com.pandoli365.bibimbap.controller;

import com.pandoli365.bibimbap.data.GameData;
import com.pandoli365.bibimbap.data.JamData;
import com.pandoli365.bibimbap.mapper.GamesMapper;
import com.pandoli365.bibimbap.mapper.JamsMapper;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

//이곳에서는 뷰만 제어
@Controller
public class WebMvcController implements WebMvcConfigurer, ErrorController {

    private static final int HUB_PAGE_SIZE = 24;

    private final GamesMapper gamesMapper;
    private final JamsMapper jamsMapper;

    public WebMvcController(GamesMapper gamesMapper, JamsMapper jamsMapper) {
        this.gamesMapper = gamesMapper;
        this.jamsMapper = jamsMapper;
    }

    @RequestMapping("/error")
    public ModelAndView errorView(HttpServletRequest request) {
        ModelAndView mv = new ModelAndView("errer");
        Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
        if (status != null) {
            mv.addObject("statusCode", status);
        }
        return mv;
    }

    /* ✅ 허용된 페이지 목록 */
    private static final List<String> ALLOWED_PAGES = Arrays.asList(
            "error", "login", "profile", "signup", "terms", "operation-policy", ""
    );

    @GetMapping("/game/new")
    public ModelAndView gameRegisterView(HttpSession session) {
        if (!isLoggedIn(session)) {
            return new ModelAndView("redirect:/login");
        }
        return new ModelAndView("game-register");
    }

    @GetMapping("/")
    public ModelAndView indexView(@RequestParam(name = "q", required = false) String query,
                                  @RequestParam(name = "cursor", required = false) String cursor) {
        return indexModelAndView(query, cursor);
    }

    @GetMapping("/{pageName}")
    public ModelAndView mainView(@PathVariable("pageName") String pageName,
                                 @RequestParam(name = "id", required = false) String id,
                                 @RequestParam(name = "q", required = false) String query,
                                 HttpSession session,
                                 HttpServletRequest request) {
        ModelAndView mv = new ModelAndView();
        if (!ALLOWED_PAGES.contains(pageName)) {
            return new ModelAndView("redirect:/error");
        }
        switch (pageName) {
            case "error":
                mv.setViewName("errer");
                Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
                if (status != null) {
                    mv.addObject("statusCode", status);
                }
                break;
            case "login":
                if (isLoggedIn(session)) {
                    return new ModelAndView("redirect:/profile");
                }
                mv.setViewName("login");
                break;
            case "profile":
                if (!isLoggedIn(session)) {
                    return new ModelAndView("redirect:/login");
                }
                mv.addObject("myGames", gamesMapper.getGamesByUserId(sessionUserId(session)));
                mv.setViewName("profile");
                break;
            case "signup":
                mv.setViewName("signup");
                break;
            case "terms":
                mv.setViewName("terms");
                break;
            case "operation-policy":
                mv.setViewName("operation-policy");
                break;
            default:
                return indexModelAndView(query, null);
        }

        return mv;
    }

    private ModelAndView indexModelAndView(String query, String cursor) {
        String normalizedQuery = query == null ? "" : query.trim();
        HubCursor parsed = parseCursor(cursor);   // null = 첫 페이지
        Integer cSort = parsed == null ? null : parsed.sortOrder();
        OffsetDateTime cCreated = parsed == null ? null : parsed.createdAt();
        Long cId = parsed == null ? null : parsed.id();

        List<GameData> rows = normalizedQuery.isBlank()
                ? gamesMapper.listVisibleKeyset(cSort, cCreated, cId, HUB_PAGE_SIZE + 1)
                : gamesMapper.searchVisibleKeyset(normalizedQuery, cSort, cCreated, cId, HUB_PAGE_SIZE + 1);

        String nextCursor = null;
        if (rows.size() > HUB_PAGE_SIZE) {
            rows = new ArrayList<>(rows.subList(0, HUB_PAGE_SIZE));
            GameData last = rows.get(HUB_PAGE_SIZE - 1);
            nextCursor = encodeCursor(last);
        }

        ModelAndView mv = new ModelAndView("index");
        mv.addObject("games", rows);
        mv.addObject("searchQuery", normalizedQuery);
        mv.addObject("nextCursor", nextCursor);   // nullable — JSP 가 null 시 더보기 미표시

        // 진행중 잼 배너 (activeCount==0 → attr 미주입 → 배너 미렌더)
        int activeCount = jamsMapper.countActive();
        if (activeCount > 0) {
            List<JamData> active = jamsMapper.listActive(1);
            if (!active.isEmpty()) {
                mv.addObject("activeJam", active.get(0));
                mv.addObject("activeJamCount", activeCount);
            }
        }
        return mv;
    }

    private String encodeCursor(GameData last) {
        if (last == null || last.getSortOrder() == null
                || last.getCreatedAt() == null || last.getId() == null) {
            return null;
        }
        return last.getSortOrder() + "_"
                + last.getCreatedAt().toInstant().toEpochMilli() + "_"
                + last.getId();
    }

    // "10_1718000000000_57" → HubCursor | null. 사용자 입력 신뢰 금지 — 형식오류는 첫 페이지 폴백.
    private HubCursor parseCursor(String cursor) {
        if (cursor == null || cursor.isBlank()) {
            return null;
        }
        String[] parts = cursor.split("_");
        if (parts.length != 3) {
            return null;
        }
        try {
            int sortOrder = Integer.parseInt(parts[0]);
            long millis = Long.parseLong(parts[1]);
            long id = Long.parseLong(parts[2]);
            OffsetDateTime createdAt = OffsetDateTime.ofInstant(
                    Instant.ofEpochMilli(millis), ZoneOffset.UTC);
            return new HubCursor(sortOrder, createdAt, id);
        } catch (NumberFormatException e) {
            return null;
        }
    }

    private record HubCursor(Integer sortOrder, OffsetDateTime createdAt, Long id) {}

    private boolean isLoggedIn(HttpSession session) {
        return session != null && session.getAttribute("userId") != null;
    }

    private long sessionUserId(HttpSession session) {
        Object userId = session.getAttribute("userId");
        if (userId instanceof Number) {
            return ((Number) userId).longValue();
        }
        if (userId instanceof String) {
            return Long.parseLong((String) userId);
        }
        throw new IllegalStateException("로그인 세션에 사용자 ID가 없습니다.");
    }

    /// 접속기기 모바일 확인 함수
    private boolean isMobileDevice(HttpServletRequest request) {
        String userAgent = request.getHeader("User-Agent");
        if (userAgent == null) {
            return false; // User-Agent가 없는 경우 기본값은 false
        }

        // 모바일 User-Agent 리스트 (대표적인 기기들)
        String[] mobileKeywords = {"Android", "iPhone", "iPad", "iPod", "Windows Phone", "Mobile", "Opera Mini", "BlackBerry"};

        // User-Agent 문자열이 모바일 기기를 포함하는지 검사
        return Arrays.stream(mobileKeywords).anyMatch(userAgent::contains);
    }
}
