package grpc

import (
	"context"
	"encoding/json"
	"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 msg struct {
	Code  string
	Email string
}

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

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

func NewServer(ctx context.Context, config *config.Config) *AuthDBServer {
	return &AuthDBServer{
		mu:          sync.Mutex{},
		users:       make(map[string]*user),
		kafkaWriter: kafka.NewWriter(ctx, topicRegistrations, net.JoinHostPort(config.Kafka.Host, strconv.Itoa(config.Kafka.Port))),
		ctx:         ctx,
		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,
		Login:     req.GetLogin(),
		Password:  code, // TODO
		Confirmed: false,
		msg: msg{
			Code:  code,
			Email: req.GetEmail(),
		},
	}
	s.users[req.Login] = user

	// TODO
	value, eru := json.Marshal(user.msg)
	if eru != nil {
		return nil, eru
	}

	//
	log.Printf("send code %s to %s ...", user.msg.Code, user.msg.Email)

	//
	err := s.kafkaWriter.WriteMessage([]byte(user.Login), value)
	if err != nil {
		log.Print(err)

		return nil, err
	}

	//
	log.Printf("send code %s to %s completed", user.msg.Code, user.msg.Email)

	return &api.RegistrationResponse{
		Code:  user.msg.Code,
		Email: user.msg.Email,
	}, 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")
}