package main

import (
	"context"
	"fmt"
	"github.com/Pena-Co-Ltd/amocrm_templategen_back/amo"
	"github.com/Pena-Co-Ltd/amocrm_templategen_back/dal"
	GDisk "github.com/Pena-Co-Ltd/amocrm_templategen_back/gdisk"
	"github.com/Pena-Co-Ltd/amocrm_templategen_back/handlers"
	"github.com/Pena-Co-Ltd/amocrm_templategen_back/middleware"
	YaDisk "github.com/Pena-Co-Ltd/amocrm_templategen_back/yadisk"
	"github.com/gorilla/mux"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
)

type Env struct {
	Domain             string `env:"DOMAIN" default:"solweb.site"`
	LogFile            string `env:"LOG_FILE" default:"./tmp/logs.log"`
	MongoUrl           string `env:"MONGO_URL" default:"mongodb+srv://user_1:cEDxC8ptLMMeoA5m@cluster0.aomle.mongodb.net/templategen"`
	DbName             string `env:"DB_NAME" default:"templategen"`
	YaDiskClientID     string `env:"YADISK_CLIENT_ID" default:"94482c181e5148c096ae6ad3b2a981ea"`
	YaDiskClientSecret string `env:"YADISK_CLIENT_SECRET" default:"7dc4f541c3f64f4a9078e59d7494d222"`
	GDiskCredentials   string `env:"GDISK_CREDENTIALS" default:"./static/gdisk-credentials.json"`
	AmoClientID        string `env:"AMO_CLIENT_ID" default:"2fc35b41-f446-4ebb-95cb-b00c55d2f810"`
	AmoClientSecret    string `env:"AMO_CLIENT_SECRET" default:"j1jtyIUPplv5Qd8raBvDP6OvvOp9KA8c2HAtkewQ8MJCaVNvob7piPFIDdjS0aiY"`
}

func main() {
	opts := GetOpts()
	fmt.Printf("OPTIONS: %+v \r\n", opts)
	ctx, cancel := context.WithCancel(context.Background())

	// Logger
	cfgLogger := zap.NewDevelopmentConfig()
	cfgLogger.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
	cfgLogger.EncoderConfig.ConsoleSeparator = "    "

	logger, err := cfgLogger.Build()

	if err != nil {
		panic(err)
	}

	// Start Data Access Layer

	mongoDal, err := dal.InitMongoDAL(ctx, opts.MongoUrl, opts.DbName, logger)
	defer mongoDal.Disconnect()
	if err != nil {
		logger.Fatal("ErrorConnectToDAL", zap.Error(err))
	}

	// Yandex Disk
	yaDisk := YaDisk.NewClientApp(
		opts.YaDiskClientID,
		opts.YaDiskClientSecret,
		fmt.Sprintf("https://%v/yadisk", opts.Domain),
		fmt.Sprintf("https://%v", opts.Domain),
	)

	// Google Drive
	gDisk, err := GDisk.NewClientApp(opts.GDiskCredentials)
	if err != nil {
		log.Fatal("ErrorCreateGoogleDriveClientApp:", err)
	}

	// Amo
	amoApp := amo.NewClientApp(
		opts.AmoClientID,
		opts.AmoClientSecret,
		fmt.Sprintf("https://%v/amo", opts.Domain))

	// Handlers
	h := handlers.NewHandlers(mongoDal, yaDisk, gDisk, amoApp, logger)

	r := mux.NewRouter()
	// Add Assets
	fs := http.FileServer(http.Dir("assets"))

	r.PathPrefix("/assets/").Handler(http.StripPrefix("/assets/", fs))
	r.PathPrefix("/tmp/generated/").Handler(http.StripPrefix("/tmp/generated/",
		http.FileServer(http.Dir("tmp/generated"))))
	r.PathPrefix("/.well-known/pki-validation/").Handler(http.StripPrefix("/.well-known/pki-validation/", http.FileServer(http.Dir(".well-known/pki-validation"))))
	r.PathPrefix("/.well-known/acme-challenge/").Handler(http.StripPrefix("/.well-known/acme-challenge/",
		http.FileServer(http.Dir(".well-known/acme-challenge"))))

	//# region ====== HANDLERS ======
	r.HandleFunc("/", h.IndexPage)
	r.HandleFunc("/pages/logout", h.LogoutPage)
	r.HandleFunc("/pages/login", h.LoginPage)
	r.HandleFunc("/pages/registration", h.RegistrationPage)
	r.HandleFunc("/pages/amoPage", h.AmoPage)
	r.NotFoundHandler = http.HandlerFunc(h.PageNotFound)

	r.HandleFunc("/amo", h.AmoSaveToken)
	r.HandleFunc("/amo/state", h.AmoState)
	//r.HandleFunc("/amo/saveToken", h.AmoSaveToken)
	r.HandleFunc("/amo/webhook", h.AmoWebhook)
	r.HandleFunc("/amo/gdisk/getTemplateDir", h.GDiskGetDirTemplate)
	r.HandleFunc("/amo/getTemplate", h.AmoGetTemplate)
	r.HandleFunc("/amo/setTemplate", h.AmoSetTemplate)
	r.HandleFunc("/amo/generateDoc", h.AmoGenerateDoc)
	r.HandleFunc("/amo/access_rules", h.AmoAccessRules)

	// Set rules for Amo end-points
	amoAccess := map[string]string{
		"/amo/webhook":     "visibility",
		"/amo/generateDoc": "creation",
	}

	r.HandleFunc("/user/registration", h.UserRegistration)
	r.HandleFunc("/user/login", h.UserLogin)
	r.HandleFunc("/user/logout", h.UserLogout)

	r.HandleFunc("/yadisk", h.YaDiskPage)
	r.HandleFunc("/yadisk/saveToken", h.YaDiskSaveToken)
	r.HandleFunc("/yadisk/getList", h.YaDiskGetList)
	r.HandleFunc("/yadisk/setFolders", h.YaDiskSetFolders)

	r.HandleFunc("/gdisk", h.GDiskPage)
	r.HandleFunc("/gdisk/saveToken", h.GDiskSaveToken)
	r.HandleFunc("/gdisk/getDirInfo", h.GDiskGetDirInfo)
	r.HandleFunc("/gdisk/getList", h.GDiskGetList)

	r.HandleFunc("/template/new", h.TemplateNew)
	r.HandleFunc("/template/getById", h.TemplateGetByID)
	r.HandleFunc("/template/getListByUser", h.TemplateGetListByUser)
	r.HandleFunc("/template/update", h.TemplateUpdate)
	r.HandleFunc("/template/delete", h.TemplateDelete)
	r.HandleFunc("/template/init", h.TemplateInit)

	//#endregion

	// Middlewares
	mw := middleware.InitMiddleware(mongoDal, amoApp, logger, amoAccess)

	r.Use(
		mw.MiddlewareHeaders,
		mw.MiddlewareCors,
		mw.MiddlewareJwt,
		mw.MiddlewareAmoJwt,
		mw.Logger,
	)

	r.NotFoundHandler = r.NewRoute().HandlerFunc(h.PageNotFound).GetHandler()

	srv := http.Server{
		Handler: r,
	}

	go func() {
		err := srv.ListenAndServe()
		if err != nil {
			logger.Fatal("CanNotServe", zap.Error(err))
		}
	}()

	// TLS

	fullCert := fmt.Sprintf("./static/cert/%v/fullchain.pem", opts.Domain)
	privCert := fmt.Sprintf("./static/cert/%v/privkey.pem", opts.Domain)

	go func() {
		err := srv.ListenAndServeTLS(fullCert, privCert)
		if err != nil {
			logger.Fatal("CanNotServe", zap.Error(err))
		}
	}()

	// Graceful Shutdown
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
	killSignal := <-interrupt
	switch killSignal {
	case os.Interrupt:
		logger.Fatal("AppInterrupted")
	case syscall.SIGTERM:
		logger.Fatal("AppTerminated")
	}

	defer cancel()
}

func GetOpts() Env {
	return getEnv(Env{})
}
