package admin

import (
	"encoding/json"
	"strings"
	"time"

	"github.com/Wei-Shaw/sub2api/internal/pkg/response"
	"github.com/Wei-Shaw/sub2api/internal/service"

	"github.com/gin-gonic/gin"
)

// KimiOAuthHandler handles Kimi OAuth admin endpoints.
type KimiOAuthHandler struct {
	kimiOAuthService *service.KimiOAuthService
	proxyRepo        service.ProxyRepository
}

// NewKimiOAuthHandler creates a new KimiOAuthHandler.
func NewKimiOAuthHandler(kimiOAuthService *service.KimiOAuthService, proxyRepo service.ProxyRepository) *KimiOAuthHandler {
	return &KimiOAuthHandler{
		kimiOAuthService: kimiOAuthService,
		proxyRepo:        proxyRepo,
	}
}

type kimiDeviceAuthRequest struct {
	ProxyID *int64 `json:"proxy_id"`
}

// InitiateDeviceAuth starts the Kimi OAuth device authorization flow.
// POST /api/v1/admin/kimi/oauth/device-auth
func (h *KimiOAuthHandler) InitiateDeviceAuth(c *gin.Context) {
	var req kimiDeviceAuthRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "Invalid request: "+err.Error())
		return
	}

	result, err := h.kimiOAuthService.InitiateDeviceAuth(c.Request.Context(), req.ProxyID, h.proxyRepo)
	if err != nil {
		response.InternalError(c, "Failed to initiate device auth: "+err.Error())
		return
	}

	response.Success(c, gin.H{
		"device_code":               result.DeviceCode,
		"user_code":                 result.UserCode,
		"verification_uri":          result.VerificationURI,
		"verification_uri_complete": result.VerificationURIComplete,
		"expires_in":                result.ExpiresIn,
		"interval":                  result.Interval,
	})
}

type kimiPollTokenRequest struct {
	DeviceCode string `json:"device_code" binding:"required"`
}

// PollToken polls for the OAuth token after device authorization.
// POST /api/v1/admin/kimi/oauth/token
func (h *KimiOAuthHandler) PollToken(c *gin.Context) {
	var req kimiPollTokenRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "Invalid request: "+err.Error())
		return
	}

	tokenInfo, err := h.kimiOAuthService.PollToken(c.Request.Context(), strings.TrimSpace(req.DeviceCode))
	if err != nil {
		msg := err.Error()
		if strings.Contains(msg, "authorization_pending") {
			response.Success(c, gin.H{
				"status":  "pending",
				"message": "Authorization pending, please complete authorization in the browser",
			})
			return
		}
		if strings.Contains(msg, "expired_token") || strings.Contains(msg, "invalid_grant") {
			response.BadRequest(c, "Token request failed: "+msg)
			return
		}
		response.InternalError(c, "Failed to poll token: "+msg)
		return
	}

	response.Success(c, gin.H{
		"status":        "completed",
		"access_token":  tokenInfo.AccessToken,
		"refresh_token": tokenInfo.RefreshToken,
		"expires_in":    tokenInfo.ExpiresIn,
		"expires_at":    tokenInfo.ExpiresAt,
		"scope":         tokenInfo.Scope,
		"token_type":    tokenInfo.TokenType,
	})
}

type kimiRefreshTokenRequest struct {
	RefreshToken string `json:"refresh_token" binding:"required"`
	ProxyID      *int64 `json:"proxy_id"`
}

// RefreshToken refreshes the Kimi access token.
// POST /api/v1/admin/kimi/oauth/refresh
func (h *KimiOAuthHandler) RefreshToken(c *gin.Context) {
	var req kimiRefreshTokenRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "Invalid request: "+err.Error())
		return
	}

	var proxyURL string
	if req.ProxyID != nil && h.proxyRepo != nil {
		if proxy, err := h.proxyRepo.GetByID(c.Request.Context(), *req.ProxyID); err == nil && proxy != nil {
			proxyURL = proxy.URL()
		}
	}

	tokenInfo, err := h.kimiOAuthService.RefreshToken(c.Request.Context(), strings.TrimSpace(req.RefreshToken), proxyURL)
	if err != nil {
		response.BadRequest(c, "Failed to refresh token: "+err.Error())
		return
	}

	response.Success(c, gin.H{
		"access_token":  tokenInfo.AccessToken,
		"refresh_token": tokenInfo.RefreshToken,
		"expires_in":    tokenInfo.ExpiresIn,
		"expires_at":    tokenInfo.ExpiresAt,
		"scope":         tokenInfo.Scope,
		"token_type":    tokenInfo.TokenType,
	})
}

// ImportLocalToken imports token from local kimi-cli credentials.
// POST /api/v1/admin/kimi/oauth/import-local
func (h *KimiOAuthHandler) ImportLocalToken(c *gin.Context) {
	var req struct {
		CredentialsJSON string `json:"credentials_json" binding:"required"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "Invalid request: "+err.Error())
		return
	}

	// Parse the local credentials JSON format
	var localCreds struct {
		AccessToken  string  `json:"access_token"`
		RefreshToken string  `json:"refresh_token"`
		ExpiresAt    float64 `json:"expires_at"`
		Scope        string  `json:"scope"`
		TokenType    string  `json:"token_type"`
		ExpiresIn    float64 `json:"expires_in"`
	}
	if err := json.Unmarshal([]byte(req.CredentialsJSON), &localCreds); err != nil {
		response.BadRequest(c, "Invalid credentials JSON: "+err.Error())
		return
	}

	if strings.TrimSpace(localCreds.AccessToken) == "" {
		response.BadRequest(c, "access_token is required in credentials")
		return
	}

	expiresAt := int64(localCreds.ExpiresAt)
	if expiresAt == 0 && localCreds.ExpiresIn > 0 {
		expiresAt = time.Now().Unix() + int64(localCreds.ExpiresIn) - service.KimiTokenSafetyWindow
	}
	minExpiresAt := time.Now().Unix() + service.KimiTokenMinTTL
	if expiresAt < minExpiresAt {
		expiresAt = minExpiresAt
	}

	response.Success(c, gin.H{
		"access_token":  localCreds.AccessToken,
		"refresh_token": localCreds.RefreshToken,
		"expires_at":    expiresAt,
		"scope":         localCreds.Scope,
		"token_type":    localCreds.TokenType,
	})
}
