package auth

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"git.slaventius.ru/test3k/auth/internal/config"
	db "git.slaventius.ru/test3k/auth/internal/transport/grpc"
	prometheus "git.slaventius.ru/test3k/auth/internal/transport/prometheus"
	"git.slaventius.ru/test3k/umate/pkg/logger"

	mux "github.com/go-chi/chi/v5"
	"github.com/golang-jwt/jwt"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"google.golang.org/grpc/status"
)

const (
	HeaderAuthDevice   string = "X-Device"      // Заголовок типа устройства
	HeaderAuthChecksum string = "X-Checksum"    // Заголовок ключа "антиспам"
	HeaderAuthToken    string = "Authorization" //
)

type ContextKey string

// ...
type AuthServer struct {
	db         *db.AuthDBClient
	prometheus *prometheus.Prometheus
	Router     *mux.Mux
	config     *config.Config
	logger     *logger.Logger
	ctx        context.Context
}

func NewServer(ctx context.Context, config *config.Config) *AuthServer {
	arouter := mux.NewMux()
	alogger := logger.NewLogger("test3k:authService", config.Sentry.DSN)
	server := &AuthServer{
		db:         db.NewDBClient(ctx, config),
		prometheus: prometheus.NewService(),
		Router:     arouter,
		config:     config,
		logger:     alogger,
		ctx:        ctx,
	}

	// эндпоинты требующие авторизации
	arouter.Get("/api/v1/healthz", server.requireAuth(healthz(server)))

	//
	arouter.Post("/api/v1/login", login(server))
	arouter.Post("/api/v1/registration", registration(server))
	arouter.Post("/api/v1/confirmation", confirmation(server))

	// middleware?
	arouter.Handle("/metrics", promhttp.Handler())

	return server
}

func (s *AuthServer) GracefulStop() error {
	return s.db.Close()
}

// Парсинг токена
func (s *AuthServer) parseToken(token string) (*jwt.Token, error) {
	return jwt.Parse(token, func(atoken *jwt.Token) (interface{}, error) {
		if _, ok := atoken.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, errors.New("there was an error in parsing")
		}

		return []byte(s.config.Auth.SecretKey), nil
	})
}

// Проверка срока жизни токена
func (s *AuthServer) verifyExpirationToken(token *jwt.Token) error {
	now := float64(time.Now().Unix())
	expiration := token.Claims.(jwt.MapClaims)["exp"].(float64)
	if expiration < now {
		return errors.New("the token has expired")
	}

	return nil
}

func (s *AuthServer) requireAuth(next http.Handler) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// ctx := r.Context()

		// Получим и проверим идентификатор сессии
		authorizationHeader := r.Header.Get(HeaderAuthToken)
		authorizationHeaderAttributes := strings.Split(authorizationHeader, " ")
		if authorizationHeader == "" || len(authorizationHeaderAttributes) < 2 {
			err := errors.New("token is empty")

			//
			s.logger.Error(err.Error())

			//
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte(err.Error()))

			return
		} else {
			tokenParsed, err := s.parseToken(authorizationHeaderAttributes[1])
			if err != nil {
				s.logger.Error(err.Error())

				//
				w.WriteHeader(http.StatusUnauthorized)
				w.Write([]byte(err.Error()))

				return
			}

			//
			erf := s.verifyExpirationToken(tokenParsed)
			if erf != nil {
				s.logger.Error(erf.Error())

				//
				w.WriteHeader(http.StatusUnauthorized)
				w.Write([]byte(erf.Error()))

				return
			}

			//
			// next.ServeHTTP(w, r.WithContext(ctx))
			next.ServeHTTP(w, r)
		}
	})
}

func healthz(s *AuthServer) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// ctx := r.Context()

		// //
		// s.logger.Info(ctx.Value(ContextKey(HeaderAuthToken)))
		// s.logger.Info(ctx.Value(ContextKey("email")))

		w.WriteHeader(http.StatusOK)
		w.Write([]byte("ok"))
	}
}

func login(s *AuthServer) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		uid := r.FormValue("uid")
		password := r.FormValue("password")
		err := s.db.Login(uid, password)
		if err != nil {
			status := status.Convert(err)

			//
			s.logger.Error(status.Message())

			//
			w.Write([]byte(status.Message()))

			return
		}

		w.Write([]byte(fmt.Sprintf("Success login for %s", uid)))
	}
}

func registration(s *AuthServer) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		uid := r.FormValue("uid")
		email := r.FormValue("email")
		password := r.FormValue("password")
		_, err := s.db.Registration(uid, email, password)
		if err != nil {
			status := status.Convert(err)

			//
			s.logger.Error(status.Message())

			//
			w.Write([]byte(status.Message()))

			return
		}

		// Увеличим счетчик заругистрировашихся пользователей
		s.prometheus.IncUsersRegistered()

		//
		w.Write([]byte(fmt.Sprintf("Success registration for %s", uid)))
	}
}

func confirmation(s *AuthServer) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		code := r.FormValue("code")
		err := s.db.Confirmation(code)
		if err != nil {
			status := status.Convert(err)

			//
			s.logger.Error(status.Message())

			//
			w.Write([]byte(status.Message()))

			return
		}

		// Увеличим счетчик пользователей подтвердивших регистрацию
		s.prometheus.IncUsersConfirmed()

		//
		w.Write([]byte(fmt.Sprintf("Success confirmation for %s", code)))
	}
}