package handlers

import (
	"encoding/json"
	"os"
	"strings"

	"gitea.d3m0k1d.ru/d3m0k1d/d3m0k1d.ru/backend/internal/auth"
	"gitea.d3m0k1d.ru/d3m0k1d/d3m0k1d.ru/backend/internal/logger"
	"gitea.d3m0k1d.ru/d3m0k1d/d3m0k1d.ru/backend/internal/repositories"
	"gitea.d3m0k1d.ru/d3m0k1d/d3m0k1d.ru/backend/internal/storage"
	"github.com/gin-gonic/gin"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/endpoints"
)

type AuthHandlers struct {
	repo        repositories.AuthRepository
	logger      *logger.Logger
	config      *oauth2.Config
	frontendURL string
}

func NewAuthHandlers(repo repositories.AuthRepository) *AuthHandlers {
	clientID := os.Getenv("GITHUB_CLIENT_ID")
	clientSecret := os.Getenv("GITHUB_CLIENT_SECRET")
	redirectURL := os.Getenv("REDIRECT_URL")
	frontendURL := os.Getenv("FRONTEND_URL")

	if clientID == "" || clientSecret == "" {
		panic("GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET must be set")
	}
	if redirectURL == "" {
		redirectURL = "http://localhost:8080/api/v1/callback/github"
	}
	if frontendURL == "" {
		frontendURL = "https://d3m0k1d.ru"
	}

	return &AuthHandlers{
		repo:        repo,
		logger:      logger.New(false),
		frontendURL: frontendURL,
		config: &oauth2.Config{
			ClientID:     clientID,
			ClientSecret: clientSecret,
			RedirectURL:  redirectURL,
			Scopes:       []string{"user:email"},
			Endpoint:     endpoints.GitHub,
		},
	}
}

// LoginGithub godoc
// @Summary Start GitHub OAuth login
// @Description Redirects to GitHub authorization
// @Tags auth
// @Success 302
// @Router /auth/github [get]
func (h *AuthHandlers) LoginGithub(c *gin.Context) {
	url := h.config.AuthCodeURL("state", oauth2.AccessTypeOnline)
	h.logger.Info("Redirect to GitHub: " + url)
	c.Redirect(302, url)
}

// CallbackGithub godoc
// @Summary GitHub OAuth callback
// @Description Exchanges authorization code for access token
// @Tags auth
// @Param code query string true "Authorization code"
// @Produce json
// @Success 200 {object} map[string]interface{} "Access token"
// @Failure 400 {object} map[string]string "Missing code"
// @Failure 500 {object} map[string]string "Exchange failed"
// @Router /callback/github [get]
func (h *AuthHandlers) CallbackGithub(c *gin.Context) {
	var id int
	h.logger.Info("CallbackGithub called")

	code := c.Query("code")
	if code == "" {
		h.logger.Error("missing code")
		c.Redirect(302, h.frontendURL+"/login?error=missing_code")
		return
	}

	h.logger.Info("Processing code: " + code[:10] + "...")

	token, err := h.config.Exchange(c.Request.Context(), code)
	if err != nil {
		h.logger.Error("Exchange failed: " + err.Error())
		c.Redirect(302, h.frontendURL+"/login?error=auth_failed")
		return
	}

	client := h.config.Client(c.Request.Context(), token)
	resp, err := client.Get("https://api.github.com/user")
	if err != nil {
		h.logger.Error("Get failed: " + err.Error())
		c.Redirect(302, h.frontendURL+"/login?error=github_api_failed")
		return
	}

	var ghUser storage.UserReg
	err = json.NewDecoder(resp.Body).Decode(&ghUser)
	if err != nil {
		h.logger.Error("Decode failed: " + err.Error())
		c.Redirect(302, h.frontendURL+"/login?error=decode_failed")
		return
	}

	isreg, err := h.repo.IsRegistered(c.Request.Context(), ghUser.GithubID)
	if err != nil {
		h.logger.Error("Database check failed: " + err.Error())
		c.Redirect(302, h.frontendURL+"/login?error=database_error")
		return
	}

	if !isreg {
		h.logger.Info("New user, registering: " + ghUser.GithubLogin)
		id, err = h.repo.Register(c.Request.Context(), ghUser)
		if err != nil {
			h.logger.Error("Registration failed: " + err.Error())
			c.Redirect(302, h.frontendURL+"/login?error=registration_failed")
			return
		}
	} else {
		h.logger.Info("Existing user, fetching data: " + ghUser.GithubLogin)
		user, err := h.repo.GetUserByGithubID(c.Request.Context(), ghUser.GithubID)
		if err != nil {
			h.logger.Error("Failed to fetch user: " + err.Error())
			c.Redirect(302, h.frontendURL+"/login?error=user_fetch_failed")
			return
		}
		id = user.ID
		ghUser.GithubLogin = user.GithubLogin
		ghUser.Email = user.Email
		ghUser.AvatarURL = user.AvatarURL
	}

	user := storage.User{
		ID:          id,
		GithubID:    ghUser.GithubID,
		GithubLogin: ghUser.GithubLogin,
		Email:       ghUser.Email,
		AvatarURL:   ghUser.AvatarURL,
	}

	jwtToken, err := auth.GenerateJWT(user)
	if err != nil {
		h.logger.Error("JWT generation failed: " + err.Error())
		c.Redirect(302, h.frontendURL+"/login?error=token_failed")
		return
	}

	h.logger.Info("Authentication successful for user: " + ghUser.GithubLogin)

	c.Redirect(302, h.frontendURL+"/auth/callback#token="+jwtToken)
}

// GetSession godoc
// @Summary Get user session
// @Description Returns user session data
// @Tags auth
// @Produce json
// @Success 200 {object} map[string]interface{} "Session data"
// @Failure 401 {object} map[string]string "Unauthorized"
// @Router /session [get]
func (h *AuthHandlers) GetSession(c *gin.Context) {
	authHeader := c.GetHeader("Authorization")
	if authHeader == "" {
		c.JSON(401, gin.H{"error": "unauthorized"})
		return
	}

	tokenString := strings.TrimPrefix(authHeader, "Bearer ")
	if tokenString == authHeader {
		c.JSON(401, gin.H{"error": "invalid authorization header"})
		return
	}

	user, err := auth.ValidateJWT(tokenString)
	if err != nil {
		c.JSON(401, gin.H{"error": "invalid token"})
		return
	}

	c.JSON(200, gin.H{
		"user": gin.H{
			"name":   user.GithubLogin,
			"email":  user.Email,
			"avatar": user.AvatarURL,
		},
	})
}
