package aiapi

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"

	"github.com/jackc/pgx/v5"

	"github.com/ultisuite/ulti-backend/internal/ai"
	"github.com/ultisuite/ulti-backend/internal/api/apiresponse"
	"github.com/ultisuite/ulti-backend/internal/apitokens"
	"github.com/ultisuite/ulti-backend/internal/users"
)

func (h *Handler) MCPProxy(w http.ResponseWriter, r *http.Request) {
	if h.cfg == nil || strings.TrimSpace(h.cfg.UltimailMCPURL) == "" {
		apiresponse.WriteError(w, r, http.StatusServiceUnavailable, apiresponse.CodeInternal, "mcp not configured", nil)
		return
	}

	token, enabledTools, err := h.resolveMCPToken(r)
	if err != nil {
		apiresponse.WriteError(w, r, http.StatusUnauthorized, apiresponse.CodeAuthUnauthorized, err.Error(), nil)
		return
	}

	target, err := url.Parse(strings.TrimRight(strings.TrimSpace(h.cfg.UltimailMCPURL), "/"))
	if err != nil {
		apiresponse.WriteError(w, r, http.StatusInternalServerError, apiresponse.CodeInternal, err.Error(), nil)
		return
	}

	proxy := httputil.NewSingleHostReverseProxy(target)
	origDirector := proxy.Director
	upstreamPath := mapMCPUpstreamPath(r.URL.Path)
	proxy.Director = func(req *http.Request) {
		origDirector(req)
		req.Host = target.Host
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		req.URL.Path = upstreamPath
		req.Header.Set("X-Ulti-Token", token)
		if len(enabledTools) > 0 {
			req.Header.Set("X-Ulti-Enabled-Tools", strings.Join(enabledTools, ","))
		}
	}
	proxy.ModifyResponse = func(resp *http.Response) error {
		resp.Header.Del("Access-Control-Allow-Origin")
		return nil
	}
	proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
		apiresponse.WriteError(w, r, http.StatusBadGateway, apiresponse.CodeInternal, err.Error(), nil)
	}

	proxy.ServeHTTP(w, r)
}

func (h *Handler) resolveMCPToken(r *http.Request) (token string, enabledTools []string, err error) {
	ctx := r.Context()
	enabledTools = h.loadEnabledTools(ctx)

	if headerToken := strings.TrimSpace(r.Header.Get("X-Ulti-Token")); headerToken != "" {
		if _, authErr := apitokens.Authenticate(ctx, h.db, headerToken); authErr == nil {
			return headerToken, enabledTools, nil
		}
	}

	if bearer := bearerToken(r); strings.HasPrefix(bearer, apitokens.TokenPrefix()) {
		if _, authErr := apitokens.Authenticate(ctx, h.db, bearer); authErr == nil {
			return bearer, enabledTools, nil
		}
	}

	if bearer := bearerToken(r); h.cfg != nil && h.cfg.AIGatewayAPIKey != "" && bearer == h.cfg.AIGatewayAPIKey {
		email := openWebUIUserEmail(r)
		if email == "" {
			return "", nil, fmt.Errorf("missing openwebui user email")
		}
		created, createErr := h.createMCPSessionForEmail(ctx, email, apitokens.ChatSessionStandalone)
		if createErr != nil {
			return "", nil, createErr
		}
		return created.TokenSecret, enabledTools, nil
	}

	if claims, ok := h.resolveClaims(r); ok && strings.TrimSpace(claims.Sub) != "" {
		created, createErr := apitokens.CreateChatSession(ctx, h.db, claims.Sub, claims.Email, apitokens.ChatSessionInput{
			Preset: apitokens.ChatSessionStandalone,
		})
		if createErr != nil {
			return "", nil, createErr
		}
		return created.TokenSecret, enabledTools, nil
	}

	return "", nil, fmt.Errorf("unauthorized")
}

func (h *Handler) createMCPSessionForEmail(ctx context.Context, email string, preset apitokens.ChatSessionPreset) (apitokens.CreatedToken, error) {
	externalID, storedEmail, err := users.LookupIdentityByEmail(ctx, h.db, email)
	if err != nil {
		if err == pgx.ErrNoRows {
			return apitokens.CreatedToken{}, fmt.Errorf("user not found")
		}
		return apitokens.CreatedToken{}, err
	}
	return apitokens.CreateChatSession(ctx, h.db, externalID, storedEmail, apitokens.ChatSessionInput{
		Preset: preset,
	})
}

func openWebUIUserEmail(r *http.Request) string {
	for _, key := range []string{
		"X-OpenWebUI-User-Email",
		"X-Ulti-User-Email",
	} {
		if email := strings.TrimSpace(r.Header.Get(key)); email != "" {
			return email
		}
	}
	return ""
}

func (h *Handler) loadEnabledTools(ctx context.Context) []string {
	deployEnabled := h.cfg != nil && h.cfg.AIAssistantEnabled
	policy, _ := ai.LoadAssistantPolicy(ctx, h.db)
	if !deployEnabled && !policy.Enabled {
		return policy.EnabledTools
	}
	if len(policy.EnabledTools) == 0 {
		return []string{"mail", "drive", "contacts", "agenda", "search", "web_search", "docs"}
	}
	return policy.EnabledTools
}

func mapMCPUpstreamPath(path string) string {
	for _, prefix := range []string{"/api/v1/ai/mcp", "/mcp"} {
		if !strings.HasPrefix(path, prefix) {
			continue
		}
		suffix := strings.TrimPrefix(path, prefix)
		if suffix == "" || suffix == "/" {
			return "/mcp"
		}
		if suffix == "/messages" || suffix == "/sse" {
			return suffix
		}
		return "/mcp" + suffix
	}
	return "/mcp"
}

func (h *Handler) publicMCPPath() string {
	return "/api/v1/ai/mcp"
}

func (h *Handler) writeMCPConfigFields(ctx context.Context, out map[string]any) {
	out["mcp_url"] = h.publicMCPPath()
	out["enabled_tools"] = h.loadEnabledTools(ctx)
}

// MCPHealth proxies ultimail-mcp /health without auth (for compose healthchecks).
func (h *Handler) MCPHealth(w http.ResponseWriter, r *http.Request) {
	if h.cfg == nil || strings.TrimSpace(h.cfg.UltimailMCPURL) == "" {
		apiresponse.WriteJSON(w, http.StatusOK, map[string]any{"ok": false, "reason": "mcp not configured"})
		return
	}
	target := strings.TrimRight(strings.TrimSpace(h.cfg.UltimailMCPURL), "/") + "/health"
	resp, err := http.Get(target)
	if err != nil {
		apiresponse.WriteJSON(w, http.StatusOK, map[string]any{"ok": false, "reason": err.Error()})
		return
	}
	defer resp.Body.Close()
	body, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(resp.StatusCode)
	_, _ = w.Write(body)
}
