package server

import (
	"fmt"
	"sync"

	"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
	"bitbucket.org/nemt/nemt-portal-api/application/entitymapping"
	"bitbucket.org/nemt/nemt-portal-api/application/notificationservice"
	"bitbucket.org/nemt/nemt-portal-api/application/tncservice"
	"bitbucket.org/nemt/nemt-portal-api/domain/contract"
	"bitbucket.org/nemt/nemt-portal-api/domain/service"
	"bitbucket.org/nemt/nemt-portal-api/infra/config"
	"bitbucket.org/nemt/nemt-portal-api/infra/errors"
	"bitbucket.org/nemt/nemt-portal-api/infra/logger"
	"bitbucket.org/nemt/nemt-portal-api/server/router"
	"bitbucket.org/nemt/nemt-portal-api/server/serverconfig"
	"github.com/labstack/echo"
)

var (
	instance *Server
	once     sync.Once
)

// Server runs the application
type Server struct {
	srv   *echo.Echo
	svc   *service.Service
	cfg   *config.Config
	log   *logger.Logger
	cache contract.CacheManager
}

// Instance returns an instance of Service
func Instance(svc *service.Service, cfg *config.Config, log *logger.Logger, cache contract.CacheManager) *Server {
	once.Do(func() {
		instance = &Server{
			srv:   echo.New(),
			svc:   svc,
			cfg:   cfg,
			log:   log,
			cache: cache,
		}
	})
	return instance
}

// Run setup and executes the server
func (s *Server) Run() error {
	s.srv.HideBanner = true

	s.srv.Debug = s.cfg.App.Debug

	entityMapper := entitymapping.New()
	notificationService := notificationservice.New(s.svc, entityMapper, s.cfg, s.cache)
	appService := applicationservice.New(s.svc, entityMapper, notificationService, s.cfg)
	tncService := tncservice.New(s.svc, entityMapper, s.cfg, notificationService)

	err := serverconfig.SetMiddlewares(s.srv, s.cfg, s.log, s.svc, appService)
	if err != nil {
		return errors.Wrap(err)
	}

	router.Register(s.srv, s.cfg, appService, tncService, notificationService)

	err = s.srv.Start(fmt.Sprintf(":%d", s.cfg.HTTP.Port))
	if err != nil {
		return errors.Wrap(err)
	}

	return nil
}
