package authapi

import (
	"encoding/json"
	"errors"
	"net/http"
	"net/url"
	"strings"

	"github.com/ultisuite/ulti-backend/internal/api/apiresponse"
	"github.com/ultisuite/ulti-backend/internal/authentik"
)

type flowCompleteRequest struct {
	ReturnTo string `json:"returnTo"`
}

type flowCompleteResponse struct {
	RedirectURL string `json:"redirectUrl"`
}

func (h *Handler) CompleteFlow(w http.ResponseWriter, r *http.Request) {
	sessionID := readFlowSessionCookie(r)
	if sessionID == "" {
		apiresponse.WriteError(w, r, http.StatusUnauthorized, "flow_session_missing", "flow session cookie required", nil)
		return
	}

	var req flowCompleteRequest
	if r.Body != nil {
		_ = json.NewDecoder(r.Body).Decode(&req)
	}
	returnTo := strings.TrimSpace(req.ReturnTo)
	if returnTo == "" {
		returnTo = "/mail/inbox"
	}
	if !strings.HasPrefix(returnTo, "/") || strings.HasPrefix(returnTo, "//") {
		apiresponse.WriteError(w, r, http.StatusBadRequest, "invalid_return_to", "returnTo must be a relative path", nil)
		return
	}

	slug := FlowAuthentication
	cookies, err := h.flows.CompleteSession(r.Context(), sessionID, slug)
	if err != nil {
		if errors.Is(err, authentik.ErrFlowSessionNotFound) {
			clearFlowSessionCookie(w)
			apiresponse.WriteError(w, r, http.StatusGone, "flow_session_expired", "flow session expired; restart the flow", nil)
			return
		}
		if errors.Is(err, authentik.ErrFlowSessionNotCompleted) {
			apiresponse.WriteError(w, r, http.StatusConflict, "flow_not_completed", "authentication flow is not finished yet", nil)
			return
		}
		if errors.Is(err, authentik.ErrFlowSessionSlugMismatch) {
			apiresponse.WriteError(w, r, http.StatusConflict, "flow_session_mismatch", "flow slug does not match active session", nil)
			return
		}
		apiresponse.WriteError(w, r, http.StatusBadGateway, "flow_complete_failed", err.Error(), nil)
		return
	}

	clearFlowSessionCookie(w)
	setBrowserAuthentikCookies(w, cookies)

	loginURL := buildLoginRedirectURL(h.appURL, returnTo)
	apiresponse.WriteJSON(w, http.StatusOK, flowCompleteResponse{RedirectURL: loginURL})
}

func buildLoginRedirectURL(appURL, returnTo string) string {
	base := strings.TrimRight(strings.TrimSpace(appURL), "/")
	if base == "" {
		base = "http://localhost:3004"
	}
	params := url.Values{}
	params.Set("returnTo", returnTo)
	return base + "/api/auth/login?" + params.Encode()
}

func setBrowserAuthentikCookies(w http.ResponseWriter, stored []authentik.SerializedCookie) {
	for _, c := range authentik.BrowserAuthentikCookies(stored) {
		http.SetCookie(w, c)
	}
}

func forwardFlowCookies(w http.ResponseWriter, stored []authentik.SerializedCookie) {
	setBrowserAuthentikCookies(w, stored)
}
