package middleware

import (
	"context"
	"net/http"
	"stocksearch/services"
	"strings"
)

const SessionCookieName = "ss_session"

// contextKey 컨텍스트 키 타입
type contextKey string

const CtxLoggedIn contextKey = "loggedIn"

// IsLoggedIn 요청 컨텍스트에서 로그인 여부 반환
func IsLoggedIn(r *http.Request) bool {
	v, _ := r.Context().Value(CtxLoggedIn).(bool)
	return v
}

// publicPaths 로그인 없이 접근 가능한 경로 (전체 일치 또는 prefix)
var publicPaths = []string{
	"/login",
	// 공개 페이지
	"/",
	"/theme",
	"/kospi200",
	"/stock/",
	// 공개 API (시세/테마/코스피200 관련)
	"/api/stock/",
	"/api/indices",
	"/api/search",
	"/api/scanner/",
	"/api/signal",
	"/api/watchlist-signal",
	"/api/themes",
	"/api/themes/",
	"/api/kospi200",
	"/api/news",
	"/api/disclosure",
	"/api/auth/check",
	"/api/watchlist",
	"/ws",
}

// isPublic 요청 경로가 공개 경로에 해당하는지 판단
func isPublic(path string) bool {
	for _, p := range publicPaths {
		if strings.HasSuffix(p, "/") {
			if strings.HasPrefix(path, p) || path == p[:len(p)-1] {
				return true
			}
		} else {
			if path == p {
				return true
			}
		}
	}
	return false
}

// Auth 세션 쿠키 검증 미들웨어
// - 공개 경로: 로그인 없이 접근 허용
// - 인증 필요 경로(자산/주문/자동매매 등): 미인증 시 페이지는 /login 리다이렉트, API는 401 반환
func Auth(sessionSvc *services.SessionService) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// 공개 경로: 인증 없이 통과하되 로그인 상태는 컨텍스트에 저장
			if isPublic(r.URL.Path) {
				cookie, err := r.Cookie(SessionCookieName)
				loggedIn := err == nil && sessionSvc.Validate(cookie.Value)
				ctx := context.WithValue(r.Context(), CtxLoggedIn, loggedIn)
				next.ServeHTTP(w, r.WithContext(ctx))
				return
			}

			// 쿠키에서 세션 ID 조회
			cookie, err := r.Cookie(SessionCookieName)
			loggedIn := err == nil && sessionSvc.Validate(cookie.Value)

			if !loggedIn {
				// API 경로는 401 JSON 반환, 페이지는 /login 리다이렉트
				if strings.HasPrefix(r.URL.Path, "/api/") {
					w.Header().Set("Content-Type", "application/json")
					w.WriteHeader(http.StatusUnauthorized)
					_, _ = w.Write([]byte(`{"error":"로그인이 필요합니다"}`))
					return
				}
				redirectURL := "/login?next=" + r.URL.RequestURI()
				http.Redirect(w, r, redirectURL, http.StatusFound)
				return
			}

			// 로그인 상태를 컨텍스트에 저장
			ctx := context.WithValue(r.Context(), CtxLoggedIn, true)
			next.ServeHTTP(w, r.WithContext(ctx))
		})
	}
}
