package main

import (
	"errors"
	"log"
	"log/slog"
	"os"
	"strconv"

	"git.kocoder.xyz/kocoded/vt/model"
	"git.kocoder.xyz/kocoded/vt/query"
	"git.kocoder.xyz/kocoded/vt/routers"
	"git.kocoder.xyz/kocoded/vt/utils"
	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/compress"
	"github.com/gofiber/fiber/v2/middleware/cors"
	"github.com/gofiber/fiber/v2/middleware/helmet"
	"github.com/gofiber/fiber/v2/middleware/idempotency"
	"github.com/gofiber/fiber/v2/middleware/logger"
	"github.com/gofiber/fiber/v2/middleware/monitor"
	"github.com/gofiber/fiber/v2/middleware/pprof"
	"github.com/gofiber/fiber/v2/middleware/recover"
	"github.com/gofiber/fiber/v2/middleware/requestid"
	"github.com/gofiber/fiber/v2/middleware/skip"
	"gorm.io/driver/postgres"
	"gorm.io/gen"
	"gorm.io/gorm"
)

func main() {
	logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

	SetupDatabase()

	app := fiber.New()

	RegisterMiddlewares(app)

	routers.RegisterAnsprechpartnerRouter(app.Group("/v1/ansprechpartner"), logger)
	routers.RegisterFirmaRouter(app.Group("/v1/firma"), logger)

	log.Fatalln(app.Listen(":3000"))
}

func RegisterMiddlewares(app *fiber.App) {
	app.Use(requestid.New())
	app.Use(compress.New())
	app.Use(helmet.New())
	app.Use(cors.New())
	// app.Use(csrf.New())
	// app.Use(healthcheck.New(healthcheck.Config{}))
	app.Use(idempotency.New())
	// app.Use(limiter.New())
	app.Use(logger.New())
	app.Use("/dbg/monitor", monitor.New())
	app.Use(pprof.New())
	app.Use(recover.New())
	app.Use(skip.New(AddPaginationParams, func(c *fiber.Ctx) bool {
		return c.Method() != fiber.MethodGet
	}))
}

func AddPaginationParams(c *fiber.Ctx) error {
	err := c.Next()
	if err != nil {
		var offset *utils.OffsetPaginationError
		if errors.As(err, &offset) {
			c.Append("X-Page", strconv.Itoa(offset.Page))
			c.Append("X-Pages", strconv.Itoa(offset.Pages))
			c.Append("X-Next-Page", strconv.Itoa(offset.NextPage))
			c.Append("X-Last-Page", strconv.Itoa(offset.LastPage))
			return nil
		}

		var keyset *utils.KeysetPaginationError
		if errors.As(err, &keyset) {
			c.Append("X-Key", strconv.Itoa(keyset.Key))
			c.Append("X-Previous-Key", strconv.Itoa(keyset.PreviousKey))
			c.Append("X-Next-Key", strconv.Itoa(keyset.NextKey))
			return nil
		}
	}
	return err
}

func SetupDatabase() {
	dsn := "host=10.1.0.2 user=vt password=20a1c7809cd065bc5afe7c36fde26abf625316c8a83cc841b435c9acf3619b1f dbname=vt port=5432 sslmode=prefer TimeZone=Europe/Vienna"
	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}

	db.SetupJoinTable(model.Ansprechpartner{}, "Firmen", model.FirmaAnsprechpartner{})
	db.SetupJoinTable(model.Firma{}, "Ansprechpartner", model.FirmaAnsprechpartner{})

	db.AutoMigrate(&model.Ansprechpartner{}, &model.FirmaAnsprechpartner{}, &model.Firma{})

	g := gen.NewGenerator(gen.Config{
		OutPath: "./query",
		Mode:    gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface, // generate mode
	})

	// gormdb, _ := gorm.Open(mysql.Open("root:@(127.0.0.1:3306)/demo?charset=utf8mb4&parseTime=True&loc=Local"))
	g.UseDB(db) // reuse your gorm db

	// Generate basic type-safe DAO API for struct `model.User` following conventions
	g.ApplyBasic(model.Ansprechpartner{}, model.Dokument{}, model.Firma{}, model.Kalender{}, model.Kalendereintrag{}, model.Kostenstelle{}, model.Lager{}, model.Lagerplatz{}, model.Material{}, model.Nachricht{}, model.Projekt{}, model.Rechnung{}, model.Rechnungsposition{}, model.Scanobject{}, model.User{}, model.Zahlung{}, model.FirmaAnsprechpartner{})

	// Generate the code
	g.Execute()

	query.SetDefault(db)
}
