package handlers

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"git.optclblast.xyz/draincloud/draincloud-core/internal/common"
	"git.optclblast.xyz/draincloud/draincloud-core/internal/domain"
	"git.optclblast.xyz/draincloud/draincloud-core/internal/handler"
	"git.optclblast.xyz/draincloud/draincloud-core/internal/logger"
	"git.optclblast.xyz/draincloud/draincloud-core/internal/storage"
	"git.optclblast.xyz/draincloud/draincloud-core/internal/storage/models"
	"github.com/google/uuid"
	"golang.org/x/crypto/bcrypt"
)

type RegisterHandler struct {
	*handler.BaseHandler
	authStorage storage.AuthStorage
}

func NewRegisterHandler(
	authStorage storage.AuthStorage,
) *RegisterHandler {
	h := &RegisterHandler{
		authStorage: authStorage,
		BaseHandler: handler.New().
			WithName("registerv1").
			WithRequiredResolveParams(),
	}
	h.WithProcessFunc(h.process)
	return h
}

func (h *RegisterHandler) process(ctx context.Context, req *common.Request, w handler.Writer) error {
	regReq := new(domain.RegisterRequest)

	if err := json.Unmarshal(req.Body, regReq); err != nil {
		return err
	}

	resp, err := h.register(ctx, regReq, w)
	if err != nil {
		return fmt.Errorf("failed to register user: %w", err)
	}

	w.Write(ctx, resp)

	return nil
}

func (d *RegisterHandler) register(
	ctx context.Context,
	req *domain.RegisterRequest,
	w handler.Writer,
) (*domain.RegisterResponse, error) {
	if err := validateLoginAndPassword(req.Login, req.Password); err != nil {
		return nil, fmt.Errorf("invalid creds: %w", err)
	}

	passwordHash, err := bcrypt.GenerateFromPassword([]byte(req.Password), 10)
	if err != nil {
		logger.Error(ctx, "[register] failed to generate password hash", logger.Err(err))
		return nil, fmt.Errorf("failed to generate password hash: %w", err)
	}

	userID, err := uuid.NewV7()
	if err != nil {
		return nil, fmt.Errorf("failed to generate user id: %w", err)
	}

	user := &models.User{
		ID:           userID,
		Username:     req.Login,
		Login:        req.Login,
		PasswordHash: passwordHash,
	}

	err = d.authStorage.AddUser(ctx, userID, user.Login, user.Username, user.PasswordHash)
	if err != nil {
		return nil, fmt.Errorf("failed to add new user: %w", err)
	}

	sessionCreatedAt := time.Now()
	sessionExpiredAt := sessionCreatedAt.Add(time.Hour * 24 * 7)

	sessionToken, err := generateSessionToken(100)
	if err != nil {
		return nil, fmt.Errorf("failed to generate a session token: %w", err)
	}
	w.SetCookie(sessionTokenCookie, sessionToken, int(sessionExpiredAt.Sub(sessionCreatedAt).Seconds()), "_path", "_domain", true, true)

	csrfToken, err := generateSessionToken(100)
	if err != nil {
		return nil, fmt.Errorf("failed to generate a csrf token: %w", err)
	}
	w.SetCookie(csrfTokenCookie, csrfToken, int(sessionExpiredAt.Sub(sessionCreatedAt).Seconds()), "_path", "_domain", true, false)

	sessionID, err := uuid.NewV7()
	if err != nil {
		return nil, fmt.Errorf("failed to generate session id: %w", err)
	}

	if _, err = d.authStorage.AddSession(ctx, &models.Session{
		ID:           sessionID,
		SessionToken: sessionToken,
		CsrfToken:    csrfToken,
		UserID:       user.ID,
		CreatedAt:    sessionCreatedAt,
		ExpiredAt:    sessionExpiredAt,
	}); err != nil {
		return nil, fmt.Errorf("failed to save session: %w", err)
	}

	return &domain.RegisterResponse{
		Ok: true,
	}, nil
}