package handlers

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

	"omixlab.com/mosis-portal/internal/api/middleware"
	"omixlab.com/mosis-portal/internal/auth"
	"omixlab.com/mosis-portal/internal/database"
)

// AuthHandler handles authentication endpoints
type AuthHandler struct {
	oauthManager *auth.OAuthManager
	jwtManager   *auth.JWTManager
	db           *database.DB
}

// NewAuthHandler creates a new auth handler
func NewAuthHandler(oauthManager *auth.OAuthManager, jwtManager *auth.JWTManager, db *database.DB) *AuthHandler {
	return &AuthHandler{
		oauthManager: oauthManager,
		jwtManager:   jwtManager,
		db:           db,
	}
}

// OAuthStart initiates OAuth flow
func (h *AuthHandler) OAuthStart(provider auth.OAuthProvider) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		state, err := auth.GenerateState()
		if err != nil {
			http.Error(w, "Failed to generate state", http.StatusInternalServerError)
			return
		}

		// Store state in cookie for verification
		http.SetCookie(w, &http.Cookie{
			Name:     "oauth_state",
			Value:    state,
			Path:     "/",
			MaxAge:   300, // 5 minutes
			HttpOnly: true,
			Secure:   r.TLS != nil,
			SameSite: http.SameSiteLaxMode,
		})

		authURL, err := h.oauthManager.GetAuthURL(provider, state)
		if err != nil {
			http.Error(w, "OAuth not configured: "+err.Error(), http.StatusNotImplemented)
			return
		}

		http.Redirect(w, r, authURL, http.StatusTemporaryRedirect)
	}
}

// OAuthCallback handles OAuth callback
func (h *AuthHandler) OAuthCallback(provider auth.OAuthProvider) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// Verify state
		stateCookie, err := r.Cookie("oauth_state")
		if err != nil || stateCookie.Value != r.URL.Query().Get("state") {
			http.Error(w, "Invalid state", http.StatusBadRequest)
			return
		}

		// Clear state cookie
		http.SetCookie(w, &http.Cookie{
			Name:   "oauth_state",
			Value:  "",
			Path:   "/",
			MaxAge: -1,
		})

		// Check for error from provider
		if errMsg := r.URL.Query().Get("error"); errMsg != "" {
			http.Error(w, "OAuth error: "+errMsg, http.StatusBadRequest)
			return
		}

		code := r.URL.Query().Get("code")
		if code == "" {
			http.Error(w, "No code provided", http.StatusBadRequest)
			return
		}

		// Exchange code for user info
		oauthUser, err := h.oauthManager.Exchange(r.Context(), provider, code)
		if err != nil {
			log.Printf("OAuth exchange failed: %v", err)
			http.Error(w, "Authentication failed", http.StatusInternalServerError)
			return
		}

		if oauthUser.Email == "" {
			http.Error(w, "Email not available from OAuth provider", http.StatusBadRequest)
			return
		}

		// Find or create developer
		developer, err := h.db.FindOrCreateDeveloper(r.Context(), &database.Developer{
			Email:         oauthUser.Email,
			Name:          oauthUser.Name,
			OAuthProvider: string(oauthUser.Provider),
			OAuthID:       oauthUser.ID,
			AvatarURL:     oauthUser.Avatar,
		})
		if err != nil {
			log.Printf("Failed to find/create developer: %v", err)
			http.Error(w, "Failed to create account", http.StatusInternalServerError)
			return
		}

		// Generate tokens
		tokenPair, err := h.jwtManager.GenerateTokenPair(developer.ID, developer.Email)
		if err != nil {
			log.Printf("Failed to generate tokens: %v", err)
			http.Error(w, "Failed to generate tokens", http.StatusInternalServerError)
			return
		}

		// Set refresh token in HttpOnly cookie
		http.SetCookie(w, &http.Cookie{
			Name:     "refresh_token",
			Value:    tokenPair.RefreshToken,
			Path:     "/",
			MaxAge:   30 * 24 * 60 * 60, // 30 days
			HttpOnly: true,
			Secure:   r.TLS != nil,
			SameSite: http.SameSiteStrictMode,
		})

		// Log the authentication
		h.db.LogAudit(r.Context(), developer.ID, "oauth_login", r.RemoteAddr, r.UserAgent(), true, "")

		// Return access token
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]interface{}{
			"access_token": tokenPair.AccessToken,
			"token_type":   tokenPair.TokenType,
			"expires_in":   tokenPair.ExpiresIn,
			"developer": map[string]interface{}{
				"id":         developer.ID,
				"email":      developer.Email,
				"name":       developer.Name,
				"avatar_url": developer.AvatarURL,
			},
		})
	}
}

// Refresh refreshes the access token using refresh token
func (h *AuthHandler) Refresh(w http.ResponseWriter, r *http.Request) {
	// Get refresh token from cookie or body
	var refreshToken string

	cookie, err := r.Cookie("refresh_token")
	if err == nil {
		refreshToken = cookie.Value
	}

	// If not in cookie, try request body
	if refreshToken == "" {
		var body struct {
			RefreshToken string `json:"refresh_token"`
		}
		if err := json.NewDecoder(r.Body).Decode(&body); err == nil {
			refreshToken = body.RefreshToken
		}
	}

	if refreshToken == "" {
		http.Error(w, "Refresh token required", http.StatusBadRequest)
		return
	}

	// Validate refresh token
	claims, err := h.jwtManager.ValidateRefreshToken(refreshToken)
	if err != nil {
		// Clear invalid cookie
		http.SetCookie(w, &http.Cookie{
			Name:   "refresh_token",
			Value:  "",
			Path:   "/",
			MaxAge: -1,
		})
		http.Error(w, "Invalid refresh token", http.StatusUnauthorized)
		return
	}

	// Generate new token pair
	tokenPair, err := h.jwtManager.GenerateTokenPair(claims.Subject, claims.Email)
	if err != nil {
		http.Error(w, "Failed to generate tokens", http.StatusInternalServerError)
		return
	}

	// Set new refresh token in cookie
	http.SetCookie(w, &http.Cookie{
		Name:     "refresh_token",
		Value:    tokenPair.RefreshToken,
		Path:     "/",
		MaxAge:   30 * 24 * 60 * 60,
		HttpOnly: true,
		Secure:   r.TLS != nil,
		SameSite: http.SameSiteStrictMode,
	})

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"access_token": tokenPair.AccessToken,
		"token_type":   tokenPair.TokenType,
		"expires_in":   tokenPair.ExpiresIn,
	})
}

// Logout invalidates the current session
func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) {
	// Clear refresh token cookie
	http.SetCookie(w, &http.Cookie{
		Name:     "refresh_token",
		Value:    "",
		Path:     "/",
		MaxAge:   -1,
		HttpOnly: true,
		Secure:   r.TLS != nil,
		SameSite: http.SameSiteStrictMode,
	})

	// Log the logout if authenticated
	developerID := middleware.GetDeveloperID(r.Context())
	if developerID != "" {
		h.db.LogAudit(r.Context(), developerID, "logout", r.RemoteAddr, r.UserAgent(), true, "")
	}

	w.WriteHeader(http.StatusNoContent)
}

// Me returns the current user's information
func (h *AuthHandler) Me(w http.ResponseWriter, r *http.Request) {
	developerID := middleware.GetDeveloperID(r.Context())
	if developerID == "" {
		http.Error(w, "Not authenticated", http.StatusUnauthorized)
		return
	}

	developer, err := h.db.GetDeveloper(r.Context(), developerID)
	if err != nil {
		http.Error(w, "Developer not found", http.StatusNotFound)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"id":         developer.ID,
		"email":      developer.Email,
		"name":       developer.Name,
		"avatar_url": developer.AvatarURL,
		"created_at": developer.CreatedAt.Format(time.RFC3339),
	})
}
