package server

import (
	"fmt"
	"net/http"
	"strings"

	"gophergate/internal/db"

	"github.com/gin-gonic/gin"
	"golang.org/x/crypto/bcrypt"
)

type ApiResponse struct {
	Success bool        `json:"success"`
	Data    interface{} `json:"data,omitempty"`
	Error   string      `json:"error,omitempty"`
}

func SuccessResponse(data interface{}) ApiResponse {
	return ApiResponse{Success: true, Data: data}
}

func ErrorResponse(err string) ApiResponse {
	return ApiResponse{Success: false, Error: err}
}

func (s *Server) adminAuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		token := strings.TrimPrefix(c.GetHeader("Authorization"), "Bearer ")
		if token == "" {
			c.AbortWithStatusJSON(http.StatusUnauthorized, ErrorResponse("Not authenticated"))
			return
		}

		session, _, err := s.sessions.ValidateSession(token)
		if err != nil {
			c.AbortWithStatusJSON(http.StatusUnauthorized, ErrorResponse("Session expired or invalid"))
			return
		}

		if session.Role != "admin" {
			c.AbortWithStatusJSON(http.StatusForbidden, ErrorResponse("Admin access required"))
			return
		}

		c.Set("session", session)
		c.Next()
	}
}

type LoginRequest struct {
	Username string `json:"username" binding:"required"`
	Password string `json:"password" binding:"required"`
}

func (s *Server) handleLogin(c *gin.Context) {
	var req LoginRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, ErrorResponse("Invalid request"))
		return
	}

	var user db.User
	err := s.database.Get(&user, "SELECT username, password_hash, display_name, role, must_change_password FROM users WHERE username = ?", req.Username)
	if err != nil {
		c.JSON(http.StatusUnauthorized, ErrorResponse("Invalid username or password"))
		return
	}

	if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password)); err != nil {
		c.JSON(http.StatusUnauthorized, ErrorResponse("Invalid username or password"))
		return
	}

	displayName := user.Username
	if user.DisplayName != nil {
		displayName = *user.DisplayName
	}

	token, err := s.sessions.CreateSession(user.Username, displayName, user.Role)
	if err != nil {
		c.JSON(http.StatusInternalServerError, ErrorResponse("Failed to create session"))
		return
	}

	c.JSON(http.StatusOK, SuccessResponse(gin.H{
		"token":                token,
		"must_change_password": user.MustChangePassword,
		"user":                 user,
	}))
}

func (s *Server) handleAuthStatus(c *gin.Context) {
	token := strings.TrimPrefix(c.GetHeader("Authorization"), "Bearer ")
	session, _, err := s.sessions.ValidateSession(token)
	if err != nil {
		c.JSON(http.StatusUnauthorized, ErrorResponse("Not authenticated"))
		return
	}

	c.JSON(http.StatusOK, SuccessResponse(gin.H{
		"authenticated": true,
		"user": gin.H{
			"username":     session.Username,
			"role":         session.Role,
			"display_name": session.DisplayName,
		},
	}))
}

type ChangePasswordRequest struct {
	CurrentPassword string `json:"current_password" binding:"required"`
	NewPassword     string `json:"new_password" binding:"required"`
}

func (s *Server) handleChangePassword(c *gin.Context) {
	token := strings.TrimPrefix(c.GetHeader("Authorization"), "Bearer ")
	session, _, err := s.sessions.ValidateSession(token)
	if err != nil {
		c.JSON(http.StatusUnauthorized, ErrorResponse("Not authenticated"))
		return
	}

	var req ChangePasswordRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, ErrorResponse("Invalid request"))
		return
	}

	var user db.User
	err = s.database.Get(&user, "SELECT password_hash FROM users WHERE username = ?", session.Username)
	if err != nil {
		c.JSON(http.StatusInternalServerError, ErrorResponse("User not found"))
		return
	}

	if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.CurrentPassword)); err != nil {
		c.JSON(http.StatusUnauthorized, ErrorResponse("Current password incorrect"))
		return
	}

	newHash, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), 12)
	if err != nil {
		c.JSON(http.StatusInternalServerError, ErrorResponse("Failed to hash new password"))
		return
	}

	_, err = s.database.Exec("UPDATE users SET password_hash = ?, must_change_password = 0 WHERE username = ?", string(newHash), session.Username)
	if err != nil {
		c.JSON(http.StatusInternalServerError, ErrorResponse("Failed to update password"))
		return
	}

	c.JSON(http.StatusOK, SuccessResponse(gin.H{"message": "Password updated successfully"}))
}

func (s *Server) handleLogout(c *gin.Context) {
	token := strings.TrimPrefix(c.GetHeader("Authorization"), "Bearer ")
	if err := s.sessions.RevokeSession(token); err != nil {
		fmt.Printf("Error revoking session: %v\n", err)
	}
	c.JSON(http.StatusOK, SuccessResponse(gin.H{"message": "Logged out"}))
}
