// Package api реализует HTTP API pulse-lets-go.
package api

import (
	"context"
	"encoding/json"
	"log"
	"net/http"
	"strings"
	"time"

	"github.com/golang-jwt/jwt/v5"
	"golang.org/x/crypto/bcrypt"

	"github.com/pulse-lets-go/internal/models"
)

// JWT claims
type claims struct {
	UserID   string `json:"user_id"`
	Username string `json:"username"`
	Role     string `json:"role"`
	jwt.RegisteredClaims
}

// contextKey — тип для ключей context.
type contextKey string

const (
	userContextKey  contextKey = "user"
	refreshTokenKey contextKey = "refresh_token"
)

// UserInfo — информация о пользователе, извлекаемая из JWT.
type UserInfo struct {
	UserID   string
	Username string
	Role     string
}

const (
	jwtExpiry     = 15 * time.Minute
	refreshExpiry = 24 * time.Hour
)

// generateJWT создаёт access+refresh токены.
func (a *API) generateJWT(user *models.User) (*models.LoginResponse, error) {
	now := time.Now()

	// Access token
	accessClaims := claims{
		UserID:   user.ID,
		Username: user.Username,
		Role:     string(user.Role),
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(now.Add(jwtExpiry)),
			IssuedAt:  jwt.NewNumericDate(now),
		},
	}
	accessToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, accessClaims).SignedString([]byte(a.jwtSecret))
	if err != nil {
		return nil, err
	}

	// Refresh token
	refreshClaims := claims{
		UserID:   user.ID,
		Username: user.Username,
		Role:     string(user.Role),
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(now.Add(refreshExpiry)),
			IssuedAt:  jwt.NewNumericDate(now),
		},
	}
	refreshToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, refreshClaims).SignedString([]byte(a.jwtSecret))
	if err != nil {
		return nil, err
	}

	return &models.LoginResponse{
		AccessToken:  accessToken,
		RefreshToken: refreshToken,
		ExpiresIn:    int64(jwtExpiry.Seconds()),
	}, nil
}

// authMiddleware — проверяет JWT в заголовке Authorization.
func (a *API) authMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		tokenStr := extractBearerToken(r)
		if tokenStr == "" {
			writeError(w, http.StatusUnauthorized, "требуется авторизация")
			return
		}

		c := &claims{}
		token, err := jwt.ParseWithClaims(tokenStr, c, func(token *jwt.Token) (interface{}, error) {
			return []byte(a.jwtSecret), nil
		})
		if err != nil || !token.Valid {
			writeError(w, http.StatusUnauthorized, "недействительный токен")
			return
		}

		user := &UserInfo{
			UserID:   c.UserID,
			Username: c.Username,
			Role:     c.Role,
		}
		ctx := context.WithValue(r.Context(), userContextKey, user)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

// adminOnly — middleware проверки роли admin.
func (a *API) adminOnly(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		user := GetUserFromContext(r.Context())
		if user == nil || user.Role != string(models.RoleAdmin) {
			writeError(w, http.StatusForbidden, "требуется роль admin")
			return
		}
		next.ServeHTTP(w, r)
	})
}

// apiKeyMiddleware — проверяет API-key для monitoring endpoint.
func (a *API) apiKeyMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		key := r.Header.Get("X-API-Key")
		if key == "" || key != a.monitoringAPIKey {
			writeError(w, http.StatusUnauthorized, "недействительный API-ключ")
			return
		}
		next.ServeHTTP(w, r)
	})
}

// handleLogin обрабатывает POST /api/auth/login.
func (a *API) handleLogin(w http.ResponseWriter, r *http.Request) {
	var req models.LoginRequest
	if err := decodeJSON(r, &req); err != nil {
		writeError(w, http.StatusBadRequest, "некорректный JSON")
		return
	}

	if req.Username == "" || req.Password == "" {
		writeError(w, http.StatusBadRequest, "имя и пароль обязательны")
		return
	}

	users, err := a.configManager.ReadUsers()
	if err != nil {
		log.Printf("[auth] ошибка чтения users.json: %v", err)
		writeError(w, http.StatusInternalServerError, "ошибка сервера")
		return
	}

	var found *models.User
	for i := range users {
		if users[i].Username == req.Username {
			found = &users[i]
			break
		}
	}

	if found == nil {
		writeError(w, http.StatusUnauthorized, "неверное имя или пароль")
		return
	}

	// Если пароль не задан (плейсхолдер), разрешаем вход с паролем "admin"
	if found.PasswordHash == "" {
		if req.Password != "admin" {
			writeError(w, http.StatusUnauthorized, "неверное имя или пароль")
			return
		}
		// Хешируем пароль и сохраняем
		hash, _ := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
		found.PasswordHash = string(hash)
		a.configManager.SaveUsers(users)
	} else {
		if err := bcrypt.CompareHashAndPassword([]byte(found.PasswordHash), []byte(req.Password)); err != nil {
			writeError(w, http.StatusUnauthorized, "неверное имя или пароль")
			return
		}
	}

	resp, err := a.generateJWT(found)
	if err != nil {
		log.Printf("[auth] ошибка генерации JWT: %v", err)
		writeError(w, http.StatusInternalServerError, "ошибка сервера")
		return
	}

	writeJSON(w, http.StatusOK, resp)
}

// handleRefresh выдаёт новый access токен по refresh токену.
func (a *API) handleRefresh(w http.ResponseWriter, r *http.Request) {
	tokenStr := extractBearerToken(r)
	if tokenStr == "" {
		writeError(w, http.StatusUnauthorized, "требуется токен")
		return
	}

	c := &claims{}
	token, err := jwt.ParseWithClaims(tokenStr, c, func(token *jwt.Token) (interface{}, error) {
		return []byte(a.jwtSecret), nil
	})
	if err != nil || !token.Valid {
		writeError(w, http.StatusUnauthorized, "недействительный refresh токен")
		return
	}

	user := &models.User{
		ID:       c.UserID,
		Username: c.Username,
		Role:     models.UserRole(c.Role),
	}

	resp, err := a.generateJWT(user)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка сервера")
		return
	}

	writeJSON(w, http.StatusOK, resp)
}

// --- Хелперы ---

// extractBearerToken извлекает JWT из заголовка Authorization: Bearer <token>.
func extractBearerToken(r *http.Request) string {
	auth := r.Header.Get("Authorization")
	if auth == "" {
		return ""
	}
	parts := strings.SplitN(auth, " ", 2)
	if len(parts) != 2 || !strings.EqualFold(parts[0], "bearer") {
		return ""
	}
	return parts[1]
}

// GetUserFromContext извлекает UserInfo из контекста запроса.
func GetUserFromContext(ctx context.Context) *UserInfo {
	user, ok := ctx.Value(userContextKey).(*UserInfo)
	if !ok {
		return nil
	}
	return user
}

// writeJSON отправляет JSON-ответ.
func writeJSON(w http.ResponseWriter, status int, v interface{}) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	json.NewEncoder(w).Encode(v)
}

// writeError отправляет JSON-ошибку.
func writeError(w http.ResponseWriter, status int, msg string) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	json.NewEncoder(w).Encode(map[string]string{"error": msg})
}
