package http_router

import (
	"errors"
	"log/slog"
	"net/http"

	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/cache"
	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/database"
	dberrors "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/database/errors"
	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/feed"
	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/integration"
	objectStorage "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/object_storage"
	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/services/agent"
	authinfra "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/services/auth_infrastructure"
	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/services/distributor"

	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/auth"
	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/auth/keycloak"
	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/config"
	"git-molva.ru/Molva/molva-backend/services/api_gateway/internal/constants"
	filemanager "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/file_manager"
	urlShortener "git-molva.ru/Molva/molva-backend/services/api_gateway/internal/url_shortener"
)

type handler struct {
	env                            string
	logger                         *slog.Logger
	urlShortener                   urlShortener.Shortener
	authManager                    auth.Manager
	authInfraService               authinfra.AuthInfrastructureService
	fileManager                    filemanager.UserFileManager
	dbClient                       database.Client
	agentService                   agent.AgentService
	distributorService             distributor.DistributorService
	cacheClient                    cache.Client
	objectStorageClient            objectStorage.Client
	jwtManager                     keycloak.TokenManager
	tempFileManager                *TempFileManager
	emailVerificationServiceAPIKey string
	feed                           *feed.Handler
	integrationClient              integration.Client
	buildConfig                    config.BuildInfo
}

type Config struct {
	env                            string
	logger                         *slog.Logger
	secretConfig                   *config.SecretConfig
	authManager                    auth.Manager
	authInfraService               authinfra.AuthInfrastructureService
	fileManager                    filemanager.UserFileManager
	dbClient                       database.Client
	agentService                   agent.AgentService
	distributorService             distributor.DistributorService
	emailVerificationServiceAPIKey string
	cacheClient                    cache.Client
	objectStorageClient            objectStorage.Client
	feed                           *feed.Handler
	integrationClient              integration.Client
	buildConfig                    config.BuildInfo
}

func newHandler(c *Config) *handler {
	tempManager, err := NewTempFileManager("")
	if err != nil {
		c.logger.Error("failed to create temp file manager", slog.String("error", err.Error()))
		return nil
	}

	return &handler{
		env:                            c.env,
		logger:                         c.logger,
		urlShortener:                   &urlShortener.LinkEncryption{Key: c.secretConfig.Key},
		authManager:                    c.authManager,
		authInfraService:               c.authInfraService,
		fileManager:                    c.fileManager,
		dbClient:                       c.dbClient,
		agentService:                   c.agentService,
		distributorService:             c.distributorService,
		emailVerificationServiceAPIKey: c.emailVerificationServiceAPIKey,
		cacheClient:                    c.cacheClient,
		objectStorageClient:            c.objectStorageClient,
		jwtManager:                     keycloak.NewJWTManager(),
		tempFileManager:                tempManager,
		feed:                           c.feed,
		integrationClient:              c.integrationClient,
		buildConfig:                    c.buildConfig,
	}
}

func (h *handler) handleKeycloakError(w http.ResponseWriter, err error) {
	switch {
	case errors.Is(err, keycloak.ErrUnknownUserType):
		http.Error(w, keycloak.ErrUnknownUserType.Error(), http.StatusBadRequest)
	case errors.Is(err, keycloak.ErrInvalidToken):
		http.Error(w, keycloak.ErrInvalidToken.Error(), http.StatusUnauthorized)
	case errors.Is(err, keycloak.ErrRealmClientUnauthorized):
		http.Error(w, "internal server error", http.StatusInternalServerError)
	case errors.Is(err, keycloak.ErrRealmClientNotFound):
		http.Error(w, "internal server error", http.StatusInternalServerError)
	case errors.Is(err, keycloak.ErrInternal):
		http.Error(w, keycloak.ErrInternal.Error(), http.StatusInternalServerError)
	case errors.Is(err, keycloak.ErrAlreadyExists):
		http.Error(w, keycloak.ErrAlreadyExists.Error(), http.StatusBadRequest)
	case errors.Is(err, keycloak.ErrBadRequest):
		http.Error(w, keycloak.ErrBadRequest.Error(), http.StatusBadRequest)
	case errors.Is(err, keycloak.ErrNotFound):
		http.Error(w, keycloak.ErrNotFound.Error(), http.StatusNotFound)
	default:
		h.logger.Error("Unhandled keycloak error", slog.String("error", err.Error()))
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
	}
}

func (h *handler) handleAuthInfraError(w http.ResponseWriter, err error, handlerName string) {
	switch {
	case errors.Is(err, authinfra.ErrBadRequest):
		http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
	case errors.Is(err, authinfra.ErrUnauthorized):
		http.Error(w, constants.ErrUnauthorized.Error(), http.StatusUnauthorized)
	case errors.Is(err, authinfra.ErrNotFound):
		http.Error(w, constants.ErrUnauthorized.Error(), http.StatusUnauthorized)
	case errors.Is(err, authinfra.ErrInternal):
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
		h.logger.Error("auth infrastructure error",
			slog.String("error", err.Error()),
			slog.String("handler", handlerName))

	default:
		h.logger.Error("Unhandled auth infrastructure error", slog.String("error", err.Error()))
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
	}
}

func (h *handler) handleDBError(w http.ResponseWriter, err error) {
	switch {
	case errors.Is(err, dberrors.ErrBadRequest):
		http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
	case errors.Is(err, dberrors.ErrForbidden):
		http.Error(w, constants.ErrForbidden.Error(), http.StatusForbidden)
	case errors.Is(err, dberrors.ErrNotFound):
		http.Error(w, constants.ErrNotFound.Error(), http.StatusNotFound)
	case errors.Is(err, dberrors.ErrInternal):
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
	default:
		h.logger.Error("Unhandled database error", slog.String("error", err.Error()))
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
	}
}

func (h *handler) handleAgentError(w http.ResponseWriter, err error) {
	switch {
	case errors.Is(err, agent.ErrBadRequest):
		http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
	case errors.Is(err, agent.ErrForbidden):
		http.Error(w, constants.ErrForbidden.Error(), http.StatusForbidden)
	case errors.Is(err, agent.ErrNotFound):
		http.Error(w, constants.ErrNotFound.Error(), http.StatusNotFound)
	case errors.Is(err, agent.ErrInternal):
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
	default:
		h.logger.Error("Unhandled agent error", slog.String("error", err.Error()))
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
	}
}

func (h *handler) handleDistributorError(w http.ResponseWriter, err error) {
	switch {
	case errors.Is(err, distributor.ErrBadRequest):
		http.Error(w, constants.ErrBadRequest.Error(), http.StatusBadRequest)
	case errors.Is(err, distributor.ErrForbidden):
		http.Error(w, constants.ErrForbidden.Error(), http.StatusForbidden)
	case errors.Is(err, distributor.ErrNotFound):
		http.Error(w, constants.ErrNotFound.Error(), http.StatusNotFound)
	case errors.Is(err, distributor.ErrInternal):
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
	default:
		h.logger.Error("Unhandled distributor error", slog.String("error", err.Error()))
		http.Error(w, constants.ErrInternalServerError.Error(), http.StatusInternalServerError)
	}
}
