package middleware

import (
	"context"
	"net/http"

	"go.uber.org/zap"

	"github.com/gocql/gocql"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config/constants"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
)

func (mid *middleware) PostJWTProcessorMiddleware(fn http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()

		// Get our authorization information.
		isAuthorized, ok := ctx.Value(constants.SessionIsAuthorized).(bool)
		if ok && isAuthorized {
			// CWE-391: Safe type assertion to prevent panic-based DoS
			// OWASP A09:2021: Security Logging and Monitoring - Prevents service crashes
			sessionID, ok := ctx.Value(constants.SessionID).(string)
			if !ok {
				mid.logger.Error("Invalid session ID type in context")
				problem := httperror.NewInternalServerError("Invalid session context")
				problem.WithInstance(r.URL.Path).
					WithTraceID(httperror.ExtractRequestID(r))
				httperror.RespondWithProblem(w, problem)
				return
			}

			// Parse the user ID from the session ID (which is actually the user ID string from JWT)
			userID, err := gocql.ParseUUID(sessionID)
			if err != nil {
				problem := httperror.NewUnauthorizedError("Invalid user ID in token")
				problem.WithInstance(r.URL.Path).
					WithTraceID(httperror.ExtractRequestID(r))
				httperror.RespondWithProblem(w, problem)
				return
			}

			// Lookup our user profile by ID or return 500 error.
			user, err := mid.userGetByIDUseCase.Execute(ctx, userID)
			if err != nil {
				// Log the actual error for debugging but return generic message to client
				mid.logger.Error("Failed to get user by ID",
					zap.Error(err),
					zap.String("user_id", userID.String()))
				problem := httperror.NewInternalServerError("Unable to verify session")
				problem.WithInstance(r.URL.Path).
					WithTraceID(httperror.ExtractRequestID(r))
				httperror.RespondWithProblem(w, problem)
				return
			}

			// If no user was found then that means our session expired and the
			// user needs to login or use the refresh token.
			if user == nil {
				problem := httperror.NewUnauthorizedError("Session expired")
				problem.WithInstance(r.URL.Path).
					WithTraceID(httperror.ExtractRequestID(r))
				httperror.RespondWithProblem(w, problem)
				return
			}

			// // If system administrator disabled the user account then we need
			// // to generate a 403 error letting the user know their account has
			// // been disabled and you cannot access the protected API endpoint.
			// if user.State == 0 {
			// 	http.Error(w, "Account disabled - please contact admin", http.StatusForbidden)
			// 	return
			// }

			// Save our user information to the context.
			// Save our user.
			ctx = context.WithValue(ctx, constants.SessionUser, user)

			// Save individual pieces of the user profile.
			ctx = context.WithValue(ctx, constants.SessionID, sessionID)
			ctx = context.WithValue(ctx, constants.SessionUserID, user.ID)
			ctx = context.WithValue(ctx, constants.SessionUserRole, user.Role)
			ctx = context.WithValue(ctx, constants.SessionUserName, user.Name)
			ctx = context.WithValue(ctx, constants.SessionUserFirstName, user.FirstName)
			ctx = context.WithValue(ctx, constants.SessionUserLastName, user.LastName)
			ctx = context.WithValue(ctx, constants.SessionUserTimezone, user.Timezone)
			// ctx = context.WithValue(ctx, constants.SessionUserStoreID, user.StoreID)
			// ctx = context.WithValue(ctx, constants.SessionUserStoreName, user.StoreName)
			// ctx = context.WithValue(ctx, constants.SessionUserStoreLevel, user.StoreLevel)
			// ctx = context.WithValue(ctx, constants.SessionUserStoreTimezone, user.StoreTimezone)
		}

		fn(w, r.WithContext(ctx))
	}
}
