package grpc

import (
	"context"
	"errors"
	"log"
	"net"
	"strconv"
	"sync"
	"test3k/authDB/internal/config"
	kafka "test3k/authDB/internal/transport/kafka"
	api "test3k/authDB/pkg/api"
	"time"
)

const (
	topicRegistrations string = "registrations" // Топик для регистраций
)

type user struct {
	ID        int32
	Code      string
	Login     string
	Password  string
	Email     string
	Confirmed bool
}

type AuthDBServer struct {
	mu          sync.Mutex
	users       map[string]*user
	kafkaWriter *kafka.KafkaWriter
	api.UnimplementedAuthDBServer
	id int32
}

func NewServer(config *config.Config) *AuthDBServer {
	return &AuthDBServer{
		mu:          sync.Mutex{},
		users:       make(map[string]*user),
		kafkaWriter: kafka.NewWriter(topicRegistrations, net.JoinHostPort(config.Kafka.Host, strconv.Itoa(config.Kafka.Port))),
		id:          0,
	}
}

func (s *AuthDBServer) GracefulStop() error {
	return s.kafkaWriter.Close()
}

func (s *AuthDBServer) Login(ctx context.Context, req *api.LoginRequest) (*api.LoginResponse, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	//
	user, ok := s.users[req.GetLogin()]
	if !ok {
		return nil, errors.New("login unknown")
	}

	//
	if !user.Confirmed {
		return nil, errors.New("login unconfirmed")
	}

	//
	if user.Password != req.Password {
		return nil, errors.New("password incorrect")
	}

	return &api.LoginResponse{
		ID: user.ID,
	}, nil
}

func (s *AuthDBServer) Registration(ctx context.Context, req *api.RegistrationRequest) (*api.RegistrationResponse, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	//
	if _, ok := s.users[req.GetLogin()]; ok {
		return nil, errors.New("login already registered")
	}

	//
	s.id = s.id + 1
	unique := time.Now().Nanosecond()
	code := strconv.Itoa(unique)

	//
	user := &user{
		ID:        s.id,
		Code:      code,
		Login:     req.GetLogin(),
		Password:  code, // TODO
		Email:     req.GetEmail(),
		Confirmed: false,
	}
	s.users[req.Login] = user

	// TODO
	err := s.kafkaWriter.WriteMessage(user.Login, user.Email)
	if err != nil {
		log.Print(err)

		return nil, err
	}

	return &api.RegistrationResponse{
		Code:  code,
		Email: req.GetEmail(),
	}, nil
}

func (s *AuthDBServer) Confirmation(ctx context.Context, req *api.ConfirmationRequest) (*api.ConfirmationResponse, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	//
	for _, x := range s.users {
		if x.Code == req.GetCode() {
			if x.Confirmed {
				return nil, errors.New("already confirmed")
			}

			//
			x.Confirmed = true

			return &api.ConfirmationResponse{
				ID: x.ID,
			}, nil
		}
	}

	return nil, errors.New("code unknown")
}