package middleware

import (
	"net/http"
	"strings"

	"haixun-backend/internal/config"
	"haixun-backend/internal/library/authctx"
	app "haixun-backend/internal/library/errors"
	"haixun-backend/internal/library/errors/code"
	authusecase "haixun-backend/internal/model/auth/domain/usecase"
	"haixun-backend/internal/response"

	"github.com/zeromicro/go-zero/rest"
)

const MemberAuthorizationHeader = "X-Member-Authorization"

// Auth validates member JWT from Authorization: Bearer <access_token>.
// Use for all protected routes except AI endpoints that reserve Authorization for provider tokens.
func Auth(tokens authusecase.TokenUseCase, cfg config.AuthConf, next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		actor, err := resolveActor(r, tokens, cfg, r.Header.Get("Authorization"), "missing bearer token")
		if err != nil {
			response.Write(r.Context(), w, nil, err)
			return
		}
		next(w, r.WithContext(authctx.WithActor(r.Context(), actor)))
	}
}

// MemberAuth validates member JWT from X-Member-Authorization.
// AI routes keep Authorization for provider API keys.
func MemberAuth(tokens authusecase.TokenUseCase, cfg config.AuthConf, next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		actor, err := resolveActor(r, tokens, cfg, r.Header.Get(MemberAuthorizationHeader), "missing member bearer token")
		if err != nil {
			response.Write(r.Context(), w, nil, err)
			return
		}
		next(w, r.WithContext(authctx.WithActor(r.Context(), actor)))
	}
}

// RestAuth wraps Auth as a go-zero route middleware.
func RestAuth(tokens authusecase.TokenUseCase, cfg config.AuthConf) rest.Middleware {
	return func(next http.HandlerFunc) http.HandlerFunc {
		return Auth(tokens, cfg, next)
	}
}

// RestMemberAuth wraps MemberAuth as a go-zero route middleware.
func RestMemberAuth(tokens authusecase.TokenUseCase, cfg config.AuthConf) rest.Middleware {
	return func(next http.HandlerFunc) http.HandlerFunc {
		return MemberAuth(tokens, cfg, next)
	}
}

func resolveActor(
	r *http.Request,
	tokens authusecase.TokenUseCase,
	cfg config.AuthConf,
	authorizationHeader string,
	missingMessage string,
) (authctx.Actor, error) {
	if cfg.DevHeaderFallback {
		tenantID := strings.TrimSpace(r.Header.Get("X-Tenant-ID"))
		uid := strings.TrimSpace(r.Header.Get("X-UID"))
		if tenantID != "" && uid != "" {
			return authctx.Actor{TenantID: tenantID, UID: uid}, nil
		}
	}
	raw := bearerToken(authorizationHeader)
	if raw == "" {
		return authctx.Actor{}, app.For(code.Auth).AuthUnauthorized(missingMessage)
	}
	if tokens == nil {
		return authctx.Actor{}, app.For(code.Auth).SysNotImplemented("token usecase is not configured")
	}
	claims, err := tokens.ParseAccessToken(r.Context(), raw)
	if err != nil {
		return authctx.Actor{}, err
	}
	return authctx.Actor{TenantID: claims.TenantID, UID: claims.UID, JTI: claims.JTI}, nil
}

func BearerToken(header string) string {
	return bearerToken(header)
}

func bearerToken(header string) string {
	const prefix = "Bearer "
	if !strings.HasPrefix(header, prefix) {
		return ""
	}
	return strings.TrimSpace(strings.TrimPrefix(header, prefix))
}
