package internal

import (
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"net/http"
	"os"
	"time"

	"github.com/perfect-panel/server/internal/report"
	"github.com/perfect-panel/server/pkg/logger"

	"github.com/perfect-panel/server/pkg/proc"
	"github.com/perfect-panel/server/pkg/trace"

	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/redis"
	"github.com/gin-gonic/gin"
	"github.com/perfect-panel/server/initialize"
	"github.com/perfect-panel/server/internal/handler"
	"github.com/perfect-panel/server/internal/middleware"
	"github.com/perfect-panel/server/internal/svc"
)

type Service struct {
	server *http.Server
	svc    *svc.ServiceContext
}

func NewService(svc *svc.ServiceContext) *Service {
	return &Service{
		svc: svc,
	}
}

func initServer(svc *svc.ServiceContext) *gin.Engine {
	// start init system config
	initStart := time.Now()
	logger.Info("system initialization start")
	initialize.StartInitSystemConfig(svc)
	logger.Infow("system initialization complete",
		logger.Field("duration", time.Since(initStart).String()),
	)
	// init gin server
	r := gin.Default()
	r.RemoteIPHeaders = []string{"X-Original-Forwarded-For", "X-Forwarded-For", "X-Real-IP"}
	// init session
	sessionStore, err := redis.NewStore(10, "tcp", svc.Config.Redis.Host, svc.Config.Redis.Pass, []byte(svc.Config.JwtAuth.AccessSecret))
	if err != nil {
		logger.Errorw("init session error", logger.Field("error", err.Error()))
		panic(err)
	}
	r.Use(sessions.Sessions("ppanel", sessionStore))
	// use cors middleware
	r.Use(
		middleware.TraceMiddleware(svc),
		middleware.ApiVersionMiddleware(svc),
		middleware.LoggerMiddleware(svc),
		middleware.SignatureMiddleware(svc),
		middleware.CorsMiddleware,
		gin.Recovery(),
	)

	// register handlers
	handler.RegisterHandlers(r, svc)
	r.StaticFile("/order-recovery.html", "./public/order-recovery.html")
	// register subscribe handler
	handler.RegisterSubscribeHandlers(r, svc)
	// register telegram handler
	handler.RegisterTelegramHandlers(r, svc)
	// register notify handler
	handler.RegisterNotifyHandlers(r, svc)
	return r
}

func (m *Service) Start() {
	if m.svc == nil {
		panic("config file path is nil")
	}

	// init service
	r := initServer(m.svc)
	// get server port
	port := m.svc.Config.Port
	host := m.svc.Config.Host
	// check gateway mode
	if report.IsGatewayMode() {
		// get free port
		freePort, err := report.ModulePort()
		if err != nil {
			logger.Errorf("get module port error: %s", err.Error())
			panic(err)
		}
		port = freePort
		host = "127.0.0.1"
		// register module
		err = report.RegisterModule(port)
		if err != nil {
			logger.Errorf("register module error: %s", err.Error())
			os.Exit(1)
		}
		logger.Infof("module registered on port %d", port)
	}

	serverAddr := fmt.Sprintf("%v:%d", host, port)
	m.server = &http.Server{
		Addr:    serverAddr,
		Handler: r,
		TLSConfig: &tls.Config{
			MinVersion: tls.VersionTLS12,
		},
	}
	traceCfg := m.svc.Config.Trace
	if traceCfg.Name == "" {
		traceCfg.Name = "ppanel"
	}
	if traceCfg.Sampler == 0 {
		traceCfg.Sampler = 1.0
	}
	trace.StartAgent(traceCfg)
	proc.AddShutdownListener(func() {
		trace.StopAgent()
	})
	m.svc.Restart = m.Restart
	logger.Infof("server start at %v", serverAddr)
	if m.svc.Config.TLS.Enable {
		if err := m.server.ListenAndServeTLS(m.svc.Config.TLS.CertFile, m.svc.Config.TLS.KeyFile); err != nil && !errors.Is(err, http.ErrServerClosed) {
			logger.Errorf("server start error: %s", err.Error())
		}
	} else {
		if err := m.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
			logger.Errorf("server start error: %s", err.Error())
		}
	}
}

func (m *Service) Stop() {
	if m.server == nil {
		return
	}
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := m.server.Shutdown(ctx); err != nil {
		logger.Errorf("server shutdown error: %s", err.Error())
	}
	logger.Info("server shutdown")
}

func (m *Service) Restart() error {
	if m.server == nil {
		return errors.New("server is nil")
	}
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := m.server.Shutdown(ctx); err != nil {
		logger.Errorf("server shutdown error: %v", err.Error())
		return err
	}
	logger.Info("server shutdown")
	go m.Start()
	return nil
}
