package handlers

import (
	"cargo-erp-backend/internal/handlers/router/activity_log"
	"cargo-erp-backend/internal/handlers/router/auth"
	"cargo-erp-backend/internal/handlers/router/basic_pricing"
	"cargo-erp-backend/internal/handlers/router/city"
	"cargo-erp-backend/internal/handlers/router/cost_component"
	"cargo-erp-backend/internal/handlers/router/district"
	"cargo-erp-backend/internal/handlers/router/moda"
	"cargo-erp-backend/internal/handlers/router/package_type"
	"cargo-erp-backend/internal/handlers/router/postal_code"
	"cargo-erp-backend/internal/handlers/router/province"
	"cargo-erp-backend/internal/handlers/router/subdistrict"
	"cargo-erp-backend/internal/middleware"
	"cargo-erp-backend/pkg/helpers"
	"cargo-erp-backend/internal/usecases"
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"go.uber.org/zap"
)

type HandlersInfterface interface {
	Run()
}
type Handlers struct {
	Helper helpers.HelperInterface
}

func NewHandlers(helper helpers.HelperInterface) HandlersInfterface {
	return &Handlers{
		Helper: helper,
	}
}
func (h *Handlers) ZapLoggerMiddleware(logger *zap.Logger) gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now()
		path := c.Request.URL.Path
		query := c.Request.URL.RawQuery

		// Lanjutkan eksekusi ke handler utama
		c.Next()

		// Hitung durasi response eksekusi
		latency := time.Since(start)
		status := c.Writer.Status()

		// Siapkan field data log terstruktur
		fields := []zap.Field{
			zap.Int("status", status),
			zap.String("method", c.Request.Method),
			zap.String("path", path),
			zap.String("query", query),
			zap.String("ip", c.ClientIP()),
			zap.Duration("latency", latency),
			zap.String("user-agent", c.Request.UserAgent()),
		}

		// Jika ada error internal dari Gin, masukkan ke log
		if len(c.Errors) > 0 {
			for _, e := range c.Errors.Errors() {
				logger.Error(e, fields...)
			}
			return
		}

		// Tentukan level log berdasarkan HTTP Status Code
		if status >= 500 {
			logger.Error("Server Error", fields...)
		} else if status >= 400 {
			logger.Warn("Client Error", fields...)
		} else {
			logger.Info("Request Success", fields...)
		}
	}
}

func (h *Handlers) CORSMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
		c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
		c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
		c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE, PATCH")

		if c.Request.Method == "OPTIONS" {
			c.AbortWithStatus(http.StatusNoContent)
			return
		}

		c.Next()
	}
}

func (h *Handlers) Run() {
	host := fmt.Sprintf("%v", h.Helper.Config().Get("app.host"))
	port := fmt.Sprintf("%v", h.Helper.Config().Get("app.port"))
	r := gin.Default()
	r.Use(h.ZapLoggerMiddleware(h.Helper.Log()))
	r.Use(gin.Recovery())
	r.Use(h.CORSMiddleware())
	r.Use(middleware.MetricsMiddleware())

	// prometheus metrics (no auth)
	r.GET("/metrics", gin.WrapH(promhttp.Handler()))

	r.GET("/ping", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})

	// serve API docs (no auth)
	docsPath := filepath.Join(".", "..", "..", "docs")
	if _, err := os.Stat(docsPath); err == nil {
		r.Static("/docs", docsPath)
	}

	//auth
	auth.NewAuthHandler(h.Helper, r).Router()

	//master-data
	masterdata := r.Group("master-data")
	masterdata.Use(middleware.NewAuthMiddleware(h.Helper).Check())
	//province
	{
		province.NewProvinceHandler(h.Helper, masterdata).Router()
		city.NewCityHandler(h.Helper, masterdata).Router()
		district.NewDistrictHandler(h.Helper, masterdata).Router()
		subdistrict.NewSubdistrictHandler(h.Helper, masterdata).Router()
		postal_code.NewPostalCodeHandler(h.Helper, masterdata).Router()
		cost_component.NewCostComponentHandler(h.Helper, masterdata).Router()
		package_type.NewPackageTypeHandler(h.Helper, masterdata).Router()
		moda.NewModaHandler(h.Helper, masterdata).Router()
	}

	//basic-pricing
	basicPricingGroup := r.Group("basic-pricings")
	basicPricingGroup.Use(middleware.NewAuthMiddleware(h.Helper).Check())
	basic_pricing.NewBasicPricingHandler(h.Helper, basicPricingGroup).Router()

	//activity-logs
	activityLogGroup := r.Group("activity-logs")
	activityLogGroup.Use(middleware.NewAuthMiddleware(h.Helper).Check())
	activity_log.NewActivityLogHandler(h.Helper, activityLogGroup).Router()

	// load cascade rules from DB
	db := h.Helper.GetDB("master")
	if err := usecases.LoadCascadeRules(db); err != nil {
		h.Helper.Log().Error("failed to load cascade rules", zap.Error(err))
	} else {
		h.Helper.Log().Info("Cascade rules loaded from database")
	}

	h.Helper.Log().Info("Starting Apps")
	r.Run(fmt.Sprintf("%v:%v", host, port))
}
