package app

import (
	"context"
	"errors"
	"fmt"
	"heruvym/dal/minio"
	"heruvym/dal/mongo"
	"heruvym/middleware"
	"heruvym/router"
	"heruvym/service"
	"heruvym/tools"
	"net/http"
	"time"

	"github.com/skeris/appInit"
	tb "gopkg.in/tucnak/telebot.v2"

	"github.com/themakers/hlog"
	"go.uber.org/zap"
)

type App struct {
	err    chan error
	logger *zap.Logger
}

const (
	ENV     = "env"
	DEFAULT = "default"
)

type Options struct {
	MongoURI         string `env:"BB_MONGO_URI" default:"mongodb://localhost:27017"`
	NumberPortLocal  string `env:"BB_PORT" default:"1488"`
	LoggerDevMode    bool   `env:"BB_IS_PROD" default:"false"`
	MinioEndpoint    string `env:"BB_MINIO_EP" default:"minio:9001"`
	MinioAccessKey   string `env:"BB_MINIO_AK" default:"minio"`
	MinioSecretKey   string `env:"BB_MINIO_SK" default:"miniostorage"`
	MinioRegion string `env:"S3_REGION" default:""`
	MinioToken       string `env:"BB_MINIO_TOKEN" default:""`
	MongoDbTable     string `env:"DATABASE_TABLE" default:"profile"`
	MongoCollections string `env:"COLLECTION_NAME" default:"profile,role"`
}

var (
	errInvalidOptions = errors.New("invalid options")
)

var _ appInit.CommonApp = (*App)(nil)

type InfoSvcStarted struct{}
type InfoSvcReady struct{}
type InfoSvcShutdown struct {
	Signal string
}
type ErrorStoring struct {
	Err error
	Uri string
}
type ErrorCanNotServe struct {
	Err error
}

var zapOptions = []zap.Option{
	zap.AddCaller(),
	zap.AddCallerSkip(2),
	zap.AddStacktrace(zap.ErrorLevel),
}

func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.CommonApp, error) {

	var (
		err       error
		zapLogger *zap.Logger
		errChan   = make(chan error)
		options   Options
		ok        bool
	)
	if options, ok = opts.(Options); !ok {
		return App{}, errInvalidOptions
	}

	if options.LoggerDevMode {
		zapLogger, err = zap.NewProduction(zapOptions...)
		if err != nil {
			return nil, err
		}
	} else {
		zapLogger, err = zap.NewDevelopment(zapOptions...)
		if err != nil {
			return nil, err
		}
	}

	zapLogger = zapLogger.With(
		zap.String("SvcCommit", ver.Commit),
		zap.String("SvcVersion", ver.Release),
		zap.String("SvcBuildTime", ver.BuildTime),
	)
	fmt.Println("logger")
	logger := hlog.New(zapLogger)
	logger.Emit(InfoSvcStarted{})

	database, err := mongo.New(
		ctx,
		options.MongoURI,
		"support",
		logger,
	)
	if err != nil {
		return nil, err
	}
	fmt.Println("mongoconnnnnnn")

	fmt.Println("mongod")
	blobStore, err := minio.New(
		ctx,
		logger,
		options.MinioEndpoint,
		options.MinioAccessKey,
		options.MinioSecretKey,
		options.MinioToken,
		options.MinioRegion,
		false)
	if err != nil {
		fmt.Println("MINIO store", err)
		return nil, err
	}
	fmt.Println("miniod")

	var newBot *tb.Bot
	if options.LoggerDevMode {
		newBot, err = tb.NewBot(tb.Settings{
			Token:     "5240336345:AAG6ZXm6IYqsIZn7SoJTltJWviOXhVa4D0c",
			Verbose:   false,
			ParseMode: tb.ModeHTML,
			Poller: &tb.LongPoller{
				Timeout: time.Second,
			},
		})
		if err != nil {
			//logger.Emit(json.Token(err))
			return nil, err
		}
	}

	heruvym := service.New(blobStore, database, logger, newBot)

	mux := router.NewRouter(map[string]http.HandlerFunc{
		"/create":            heruvym.CreateTicket,
		"/subscribe":         tools.SseWrapper(heruvym.GetList),
		"/ticket":            tools.SseWrapper(heruvym.Subscribe),
		"/send":              tools.HandlerWrapper(heruvym.PutMessage),
		"/requestScreenshot": tools.HandlerWrapper(heruvym.RequestScreenshot),
		"/sendFiles":         heruvym.PutFile,
		"/sendSC":            heruvym.PutSC,
		"/getTickets":        tools.HandlerWrapper(heruvym.GetTickets),
		"/getMessages":       tools.HandlerWrapper(heruvym.GetMessages),
		"/pick":              tools.HandlerWrapper(heruvym.Pick),
		"/delegate":          tools.HandlerWrapper(heruvym.Delegate),
		"/vote":              tools.HandlerWrapper(heruvym.Vote),
		"/close":             tools.HandlerWrapper(heruvym.CloseTicket),
	})

	mw := middleware.NewMiddleware(
		logger,
		"*",
		nil,
	)

	mux.Use(
		func(handler http.Handler) http.Handler {
			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				w.Header().Set("Access-Control-Allow-Origin", "*")
				w.Header().Set("Access-Control-Allow-Credentials", "true")
				w.Header().Set("Access-Control-Allow-Headers", "*")
				w.Header().Set("Access-Control-Expose-Headers", "*")
				if r.Method == http.MethodOptions {
					return
				}
				handler.ServeHTTP(w, r)
			})
		},
		mw.MiddlewareLogger,
		//mw.MiddlewareOriginAccess,
		mw.MiddlewareRecovery,
		mw.MiddlewareJwt,
		mw.MiddlewareGetJwt,
		//mw.MiddlewareJwtPlug,
		//mw.MiddlewareRoleAccess,
	)
	fmt.Println("mux")
	server := &http.Server{
		Handler: mux,
		Addr:    fmt.Sprintf(":%s", options.NumberPortLocal),
	}

	go func() {
		defer func() {
			if err := server.Shutdown(ctx); err != nil {
				errChan <- err
			}
		}()

		logger.Emit(InfoSvcStarted{})
		tmplKey := "%s.key"
		tmplCrt := "%s.crt"

		tmplCrt = fmt.Sprintf(tmplCrt, "prod")
		tmplKey = fmt.Sprintf(tmplKey, "prod")

		fmt.Println("ISPROd", options.LoggerDevMode)
		if options.LoggerDevMode {
			if err := server.ListenAndServeTLS(tmplCrt, tmplKey); err != nil {
				logger.Emit(ErrorCanNotServe{
					Err: err,
				})
				errChan <- err
			}
		} else {
			fmt.Println("startserver" + options.NumberPortLocal)
			if err := server.ListenAndServe(); err != nil {
				errChan <- err
			}
		}
	}()

	return &App{
		logger: zapLogger,
		err:    errChan,
	}, nil
}

func (a App) GetLogger() *zap.Logger {
	return a.logger
}

func (a App) GetErr() chan error {
	return a.err
}
