package lending

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gothinkster/golang-gin-realworld-example-app/common"
	"github.com/gothinkster/golang-gin-realworld-example-app/models"
)

// GetUserPosition returns user's lending position
// GET /api/lending/position/:address
func GetUserPosition(c *gin.Context) {
	address := c.Param("address")

	if address == "" {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"error":   "wallet address is required",
		})
		return
	}

	config := GetLendingMarketConfig()
	position := &UserPosition{
		UserAddress:        address,
		WalletAddress:      address,
		SuppliedBalance:    "0",
		SuppliedBalanceUSD: 0,
		BorrowedBalance:    "0",
		BorrowedBalanceUSD: 0,
		CollateralBalances: map[string]CollateralInfo{},
		HealthFactor:       0,
		LTV:                0,
		SupplyAPY:          config["base_supply_apy"].(float64),
		BorrowAPY:          config["base_borrow_apy"].(float64),
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"data":    position,
	})
}

// GetLendingStats returns lending market statistics
// GET /api/lending/stats
func GetLendingStats(c *gin.Context) {
	config := GetLendingMarketConfig()
	stats := &LendingStats{
		TotalSuppliedUSD:   0,
		TotalBorrowedUSD:   0,
		TotalCollateralUSD: 0,
		UtilizationRate:    0,
		AvgSupplyAPY:       config["base_supply_apy"].(float64),
		AvgBorrowAPY:       config["base_borrow_apy"].(float64),
		TotalUsers:         0,
		ActiveBorrowers:    0,
		TotalTVL:           0,
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"data":    stats,
	})
}

// GetLendingMarkets returns lending market configuration.
// Contract addresses come from system_contracts; static config from GetLendingMarketConfig.
// GET /api/lending/markets
func GetLendingMarkets(c *gin.Context) {
	db := common.GetDB()

	var lc struct {
		Address string `gorm:"column:address"`
		ChainID int    `gorm:"column:chain_id"`
	}
	db.Table("system_contracts").
		Where("name = ? AND is_active = ?", "lendingProxy", true).
		Select("address, chain_id").
		First(&lc)

	config := GetLendingMarketConfig()
	config["contract_address"] = lc.Address
	config["chain_id"] = lc.ChainID

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"data":    []interface{}{config},
	})
}

// SupplyUSDC handles USDC supply (deposit) transaction
// POST /api/lending/supply
func SupplyUSDC(c *gin.Context) {
	var req SupplyRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"error":   err.Error(),
		})
		return
	}

	// TODO: Validate transaction on blockchain
	// TODO: Update user position
	// TODO: Record transaction in database

	c.JSON(http.StatusOK, SupplyResponse{
		Success: true,
		Message: "USDC supplied successfully",
	})
}

// WithdrawUSDC handles USDC withdrawal transaction
// POST /api/lending/withdraw
func WithdrawUSDC(c *gin.Context) {
	var req WithdrawRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"error":   err.Error(),
		})
		return
	}

	// TODO: Validate transaction on blockchain
	// TODO: Update user position
	// TODO: Record transaction in database

	c.JSON(http.StatusOK, WithdrawResponse{
		Success: true,
		Message: "USDC withdrawn successfully",
	})
}

// SupplyCollateral handles collateral supply transaction
// POST /api/lending/supply-collateral
func SupplyCollateral(c *gin.Context) {
	var req SupplyCollateralRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"error":   err.Error(),
		})
		return
	}

	// Validate asset type - check against assets table
	if !ValidateYTToken(req.Asset) {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"error":   "invalid collateral asset, must be a valid YT token (YT-A, YT-B, YT-C)",
		})
		return
	}

	// Get token info from database
	tokenInfo, err := GetYTTokenInfo(req.Asset, 0)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "failed to fetch token information",
		})
		return
	}
	_ = tokenInfo // Will be used for blockchain validation

	// TODO: Validate transaction on blockchain using tokenInfo.ContractAddress
	// TODO: Update user collateral position
	// TODO: Record transaction in database

	c.JSON(http.StatusOK, SupplyCollateralResponse{
		Success: true,
		Message: "Collateral supplied successfully",
	})
}

// WithdrawCollateral handles collateral withdrawal transaction
// POST /api/lending/withdraw-collateral
func WithdrawCollateral(c *gin.Context) {
	var req WithdrawCollateralRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"error":   err.Error(),
		})
		return
	}

	// Validate asset type - check against assets table
	if !ValidateYTToken(req.Asset) {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"error":   "invalid collateral asset, must be a valid YT token (YT-A, YT-B, YT-C)",
		})
		return
	}

	// Get token info from database
	tokenInfo, err := GetYTTokenInfo(req.Asset, 0)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"success": false,
			"error":   "failed to fetch token information",
		})
		return
	}
	_ = tokenInfo // Will be used for blockchain validation

	// TODO: Validate transaction on blockchain using tokenInfo.ContractAddress
	// TODO: Check if withdrawal is safe (health factor)
	// TODO: Update user collateral position
	// TODO: Record transaction in database

	c.JSON(http.StatusOK, WithdrawCollateralResponse{
		Success: true,
		Message: "Collateral withdrawn successfully",
	})
}

// BorrowUSDC handles USDC borrow transaction
// POST /api/lending/borrow
func BorrowUSDC(c *gin.Context) {
	var req BorrowRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"error":   err.Error(),
		})
		return
	}

	// TODO: Validate transaction on blockchain
	// TODO: Check collateral sufficiency
	// TODO: Update user borrow position
	// TODO: Record transaction in database

	c.JSON(http.StatusOK, BorrowResponse{
		Success: true,
		Message: "USDC borrowed successfully",
	})
}

// RepayUSDC handles USDC repayment transaction
// POST /api/lending/repay
func RepayUSDC(c *gin.Context) {
	var req RepayRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"success": false,
			"error":   err.Error(),
		})
		return
	}

	// TODO: Validate transaction on blockchain
	// TODO: Update user borrow position
	// TODO: Record transaction in database

	c.JSON(http.StatusOK, RepayResponse{
		Success: true,
		Message: "USDC repaid successfully",
	})
}

// GetLendingAPYHistory returns historical supply/borrow APY snapshots
// GET /api/lending/apy-history?period=1W&chain_id=97
func GetLendingAPYHistory(c *gin.Context) {
	period := c.DefaultQuery("period", "1W")
	chainId := parseChainID(c)

	database := common.GetDB()

	var usdcAsset models.Asset
	if err := database.Where("asset_code = ?", "USDC").First(&usdcAsset).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "USDC asset not configured"})
		return
	}

	now := time.Now()
	var since time.Time
	switch period {
	case "1M":
		since = now.AddDate(0, -1, 0)
	case "1Y":
		since = now.AddDate(-1, 0, 0)
	default: // 1W
		since = now.AddDate(0, 0, -7)
	}

	query := database.Where("asset_id = ? AND snapshot_time >= ?", usdcAsset.ID, since).
		Order("snapshot_time ASC")
	if chainId != 0 {
		query = query.Where("chain_id = ?", chainId)
	}

	var snapshots []models.APYSnapshot
	if err := query.Find(&snapshots).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "failed to fetch APY history"})
		return
	}

	type DataPoint struct {
		Time      string  `json:"time"`
		SupplyAPY float64 `json:"supply_apy"`
		BorrowAPY float64 `json:"borrow_apy"`
	}

	points := make([]DataPoint, 0, len(snapshots))
	for _, s := range snapshots {
		points = append(points, DataPoint{
			Time:      s.SnapshotTime.UTC().Format(time.RFC3339),
			SupplyAPY: s.SupplyAPY,
			BorrowAPY: s.BorrowAPY,
		})
	}

	var currentSupplyAPY, currentBorrowAPY float64
	if len(snapshots) > 0 {
		last := snapshots[len(snapshots)-1]
		currentSupplyAPY = last.SupplyAPY
		currentBorrowAPY = last.BorrowAPY
	}

	// APY change vs first point in period
	var apyChange float64
	if len(snapshots) > 1 {
		apyChange = snapshots[len(snapshots)-1].SupplyAPY - snapshots[0].SupplyAPY
	}

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"data": gin.H{
			"history":            points,
			"current_supply_apy": currentSupplyAPY,
			"current_borrow_apy": currentBorrowAPY,
			"apy_change":         apyChange,
			"period":             period,
		},
	})
}
