package main

import (
	"log"

	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"

	"github.com/gothinkster/golang-gin-realworld-example-app/admin"
	"github.com/gothinkster/golang-gin-realworld-example-app/alp"
	"github.com/gothinkster/golang-gin-realworld-example-app/articles"
	"github.com/gothinkster/golang-gin-realworld-example-app/common"
	"github.com/gothinkster/golang-gin-realworld-example-app/config"
	"github.com/gothinkster/golang-gin-realworld-example-app/fundmarket"
	"github.com/gothinkster/golang-gin-realworld-example-app/holders"
	"github.com/gothinkster/golang-gin-realworld-example-app/lending"
	"github.com/gothinkster/golang-gin-realworld-example-app/middleware"
	"github.com/gothinkster/golang-gin-realworld-example-app/models"
	"github.com/gothinkster/golang-gin-realworld-example-app/points"
	"github.com/gothinkster/golang-gin-realworld-example-app/users"
	"gorm.io/gorm"
)

func Migrate(db *gorm.DB) {
	// Original tables
	users.AutoMigrate()
	db.AutoMigrate(&articles.ArticleModel{})
	db.AutoMigrate(&articles.TagModel{})
	db.AutoMigrate(&articles.FavoriteModel{})
	db.AutoMigrate(&articles.ArticleUserModel{})
	db.AutoMigrate(&articles.CommentModel{})

	// AssetX core tables
	db.AutoMigrate(&models.User{})
	db.AutoMigrate(&models.HolderSnapshot{})
	db.AutoMigrate(&models.Asset{})
	db.AutoMigrate(&models.AssetPerformance{})
	db.AutoMigrate(&models.APYSnapshot{})
	db.AutoMigrate(&models.AssetCustody{})
	db.AutoMigrate(&models.AssetAuditReport{})

	// Config / content tables
	db.AutoMigrate(&models.SystemContract{})
	db.AutoMigrate(&models.ProductLink{})

	// Points system tables
	db.AutoMigrate(&models.PointsRule{})
	db.AutoMigrate(&models.Season{})
	db.AutoMigrate(&models.VIPTier{})
	db.AutoMigrate(&models.InviteCode{})
	db.AutoMigrate(&models.Invitation{})
	db.AutoMigrate(&models.UserPointsSummary{})
	db.AutoMigrate(&models.UserPointsRecord{})
	db.AutoMigrate(&models.UserTeam{})

	// ALP pool snapshots
	db.AutoMigrate(&models.ALPSnapshot{})

	// Liquidation bot records
	db.AutoMigrate(&models.LiquidationRecord{})
	db.AutoMigrate(&models.KnownBorrower{})

	// Collateral buyer bot records
	db.AutoMigrate(&models.CollateralBuyRecord{})

	// Scanner state: drop old single-column unique index, then migrate with composite index
	db.Exec("ALTER TABLE scanner_state DROP INDEX `uni_chain_id`") // ignore error if not exists
	db.AutoMigrate(&models.ScannerState{})

	// YT Swap event records (for "Your Total Earning" calculation)
	db.AutoMigrate(&models.YTSwapRecord{})

	// Audit / ops
	db.AutoMigrate(&models.OperationLog{})

	log.Println("✓ Database migration completed")
}

func main() {
	cfg := config.Load()
	log.Println("✓ Configuration loaded")

	var db *gorm.DB
	if cfg.DBType == "mysql" {
		db = common.InitMySQL()
	} else {
		db = common.Init()
	}

	Migrate(db)

	sqlDB, err := db.DB()
	if err != nil {
		log.Println("failed to get sql.DB:", err)
	} else {
		defer sqlDB.Close()
	}

	common.InitRedis()

	// Start holder scanner (continuous block scanning)
	go holders.StartAllScanners()

	// Start background snapshot services (run every hour)
	go fundmarket.StartPriceSnapshot(cfg)
	go alp.StartALPSnapshot(cfg)
	go lending.StartLendingAPYSnapshot(cfg)

	// Start liquidation bot (requires LIQUIDATOR_PRIVATE_KEY)
	go lending.StartLiquidationBot(cfg)

	// Start collateral buyer bot (requires COLLATERAL_BUYER_PRIVATE_KEY)
	go lending.StartCollateralBuyerBot(cfg)

	if cfg.GinMode == "release" {
		gin.SetMode(gin.ReleaseMode)
	}
	r := gin.Default()

	r.Use(cors.New(cors.Config{
		AllowAllOrigins:  true,
		AllowMethods:     []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
		AllowHeaders:     []string{"Origin", "Content-Type", "Authorization"},
		ExposeHeaders:    []string{"Content-Length"},
		AllowCredentials: false,
	}))

	r.RedirectTrailingSlash = false

	// Serve uploaded files
	r.Static("/uploads", "./uploads")

	v1 := r.Group("/api")

	// Public routes
	users.UsersRegister(v1.Group("/users"))
	articles.ArticlesAnonymousRegister(v1.Group("/articles"))
	articles.TagsAnonymousRegister(v1.Group("/tags"))
	users.ProfileRetrieveRegister(v1.Group("/profiles"))

	v1.GET("/holders/stats", holders.GetStats)
	v1.GET("/holders/:tokenType", holders.GetHoldersByType)

	v1.GET("/contracts", admin.GetContracts)

	v1.GET("/fundmarket/products", fundmarket.GetProducts)
	v1.GET("/fundmarket/stats", fundmarket.GetStats)
	v1.GET("/fundmarket/products/:id", fundmarket.GetProductByID)
	v1.GET("/fundmarket/products/:id/history", fundmarket.GetProductHistory)
		v1.GET("/fundmarket/products/:id/daily-returns", fundmarket.GetDailyReturns)
	v1.GET("/fundmarket/net-deposited", fundmarket.GetNetDeposited)

	v1.GET("/alp/stats", alp.GetALPStats)
		v1.GET("/alp/history", alp.GetALPHistory)

	v1.GET("/lending/position/:address", lending.GetUserPosition)
	v1.GET("/lending/stats", lending.GetLendingStats)
	v1.GET("/lending/markets", lending.GetLendingMarkets)
	v1.GET("/lending/apy-history", lending.GetLendingAPYHistory)
	v1.GET("/lending/tokens", lending.GetTokensInfo)
	v1.GET("/lending/tokens/:assetCode", lending.GetTokenInfo)
	v1.POST("/lending/supply", lending.SupplyUSDC)
	v1.POST("/lending/withdraw", lending.WithdrawUSDC)
	v1.POST("/lending/supply-collateral", lending.SupplyCollateral)
	v1.POST("/lending/withdraw-collateral", lending.WithdrawCollateral)
	v1.POST("/lending/borrow", lending.BorrowUSDC)
	v1.POST("/lending/repay", lending.RepayUSDC)

	v1.POST("/points/wallet-register", points.WalletRegister)
	v1.GET("/points/dashboard", points.GetDashboard)
	v1.GET("/points/leaderboard", points.GetLeaderboard)
	v1.GET("/points/invite-code", points.GetInviteCode)
	v1.POST("/points/bind-invite", points.BindInvite)
	v1.GET("/points/team", points.GetTeamTVL)
	v1.GET("/points/activities", points.GetActivities)

	// Admin API (has its own auth + RequireAdmin middleware internally)
	admin.RegisterRoutes(v1)

	// Protected routes
	v1.Use(middleware.AuthMiddleware(true))
	{
		users.UserRegister(v1.Group("/user"))
		users.ProfileRegister(v1.Group("/profiles"))
		articles.ArticlesRegister(v1.Group("/articles"))

		holdersGroup := v1.Group("/holders")
		holdersGroup.POST("/update", middleware.RequireAdmin(), holders.UpdateHolders)
	}

	r.GET("/api/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{"message": "pong", "version": "1.0.0-assetx"})
	})

	port := ":" + cfg.Port
	log.Printf("✓ Server starting on port %s\n", cfg.Port)
	if err := r.Run(port); err != nil {
		log.Fatal("failed to start server:", err)
	}
}
