package router

import (
	"cursor-api-proxy/internal/config"
	"cursor-api-proxy/internal/handlers"
	"cursor-api-proxy/internal/httputil"
	"cursor-api-proxy/internal/logger"
	"cursor-api-proxy/internal/pool"
	"fmt"
	"net/http"
	"os"
	"time"
)

type RouterOptions struct {
	Version     string
	Config      config.BridgeConfig
	ModelCache  *handlers.ModelCacheRef
	LastModel   *string
	Pool        pool.PoolHandle
}

func NewRouter(opts RouterOptions) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		cfg := opts.Config
		pathname := r.URL.Path
		method := r.Method
		remoteAddress := r.RemoteAddr
		if r.Header.Get("X-Real-IP") != "" {
			remoteAddress = r.Header.Get("X-Real-IP")
		}

		logger.LogIncoming(method, pathname, remoteAddress)

		defer func() {
			logger.AppendSessionLine(cfg.SessionsLogPath, method, pathname, remoteAddress, 200)
		}()

		if cfg.RequiredKey != "" {
			token := httputil.ExtractBearerToken(r)
			if token != cfg.RequiredKey {
				httputil.WriteJSON(w, 401, map[string]interface{}{
					"error": map[string]string{"message": "Invalid API key", "code": "unauthorized"},
				}, nil)
				return
			}
		}

		switch {
		case method == "GET" && pathname == "/health":
			handlers.HandleHealth(w, r, opts.Version, cfg)

		case method == "GET" && pathname == "/v1/models":
			opts.ModelCache.HandleModels(w, r, cfg)

		case method == "POST" && pathname == "/v1/chat/completions":
			raw, err := httputil.ReadBody(r)
			if err != nil {
				httputil.WriteJSON(w, 400, map[string]interface{}{
					"error": map[string]string{"message": "failed to read body", "code": "bad_request"},
				}, nil)
				return
			}
			handlers.HandleChatCompletions(w, r, cfg, opts.Pool, opts.LastModel, raw, method, pathname, remoteAddress)

		case method == "POST" && pathname == "/v1/messages":
			raw, err := httputil.ReadBody(r)
			if err != nil {
				httputil.WriteJSON(w, 400, map[string]interface{}{
					"error": map[string]string{"message": "failed to read body", "code": "bad_request"},
				}, nil)
				return
			}
			handlers.HandleAnthropicMessages(w, r, cfg, opts.Pool, opts.LastModel, raw, method, pathname, remoteAddress)

		case (method == "POST" || method == "GET") && pathname == "/v1/completions":
			httputil.WriteJSON(w, 404, map[string]interface{}{
				"error": map[string]string{
					"message": "Legacy completions endpoint is not supported. Use POST /v1/chat/completions instead.",
					"code":    "not_found",
				},
			}, nil)

		case pathname == "/v1/embeddings":
			httputil.WriteJSON(w, 404, map[string]interface{}{
				"error": map[string]string{
					"message": "Embeddings are not supported by this proxy.",
					"code":    "not_found",
				},
			}, nil)

		default:
			httputil.WriteJSON(w, 404, map[string]interface{}{
				"error": map[string]string{"message": "Not found", "code": "not_found"},
			}, nil)
		}
	}
}

func recoveryMiddleware(logPath string, next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if rec := recover(); rec != nil {
				msg := fmt.Sprintf("%v", rec)
				fmt.Fprintf(os.Stderr, "[%s] Proxy panic: %s\n", time.Now().UTC().Format(time.RFC3339), msg)
				line := fmt.Sprintf("%s ERROR %s %s %s %s\n",
					time.Now().UTC().Format(time.RFC3339), r.Method, r.URL.Path, r.RemoteAddr,
					msg[:min(200, len(msg))])
				if f, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err == nil {
					_, _ = f.WriteString(line)
					f.Close()
				}
				if !isHeaderWritten(w) {
					httputil.WriteJSON(w, 500, map[string]interface{}{
						"error": map[string]string{"message": msg, "code": "internal_error"},
					}, nil)
				}
			}
		}()
		next(w, r)
	}
}

func isHeaderWritten(w http.ResponseWriter) bool {
	// Can't reliably detect without wrapping; always try to write
	return false
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func WrapWithRecovery(logPath string, handler http.HandlerFunc) http.HandlerFunc {
	return recoveryMiddleware(logPath, handler)
}
