package handlers

import (
	"fmt"
	"net/http"
	"time"

	"goyco/internal/config"
	"goyco/internal/health"
	"goyco/internal/middleware"
	"goyco/internal/repositories"
	"goyco/internal/services"
	"goyco/internal/version"

	"github.com/go-chi/chi/v5"
	"gorm.io/gorm"
)

type APIHandler struct {
	config           *config.Config
	postRepo         repositories.PostRepository
	userRepo         repositories.UserRepository
	voteService      *services.VoteService
	dbMonitor        middleware.DBMonitor
	healthChecker    *health.CompositeChecker
	metricsCollector *middleware.MetricsCollector
}

func NewAPIHandler(config *config.Config, postRepo repositories.PostRepository, userRepo repositories.UserRepository, voteService *services.VoteService) *APIHandler {
	return &APIHandler{
		config:      config,
		postRepo:    postRepo,
		userRepo:    userRepo,
		voteService: voteService,
	}
}

func NewAPIHandlerWithMonitoring(config *config.Config, postRepo repositories.PostRepository, userRepo repositories.UserRepository, voteService *services.VoteService, db *gorm.DB, dbMonitor middleware.DBMonitor) *APIHandler {
	if db == nil {
		return NewAPIHandler(config, postRepo, userRepo, voteService)
	}

	sqlDB, err := db.DB()
	if err != nil {
		return NewAPIHandler(config, postRepo, userRepo, voteService)
	}

	compositeChecker := health.NewCompositeChecker()

	dbChecker := health.NewDatabaseChecker(sqlDB, dbMonitor)
	compositeChecker.AddChecker(dbChecker)

	smtpConfig := health.SMTPConfig{
		Host:     config.SMTP.Host,
		Port:     config.SMTP.Port,
		Username: config.SMTP.Username,
		Password: config.SMTP.Password,
		From:     config.SMTP.From,
	}
	smtpChecker := health.NewSMTPChecker(smtpConfig)
	compositeChecker.AddChecker(smtpChecker)

	metricsCollector := middleware.NewMetricsCollector(dbMonitor)

	return &APIHandler{
		config:           config,
		postRepo:         postRepo,
		userRepo:         userRepo,
		voteService:      voteService,
		dbMonitor:        dbMonitor,
		healthChecker:    compositeChecker,
		metricsCollector: metricsCollector,
	}
}

type APIInfo = CommonResponse

// @Summary Get API information
// @Description Retrieve API metadata, available endpoints, authentication details, and response formats
// @Tags api
// @Accept json
// @Produce json
// @Success 200 {object} APIInfo "API information retrieved successfully"
// @Router /api [get]
func (h *APIHandler) GetAPIInfo(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/api" {
		http.NotFound(w, r)
		return
	}

	apiInfo := map[string]any{
		"name":        fmt.Sprintf("%s API", h.config.App.Title),
		"version":     version.GetVersion(),
		"description": "Y Combinator-style news board API",
		"endpoints": map[string]any{
			"authentication": map[string]any{
				"POST /api/auth/register":            "Register new user",
				"POST /api/auth/login":               "Login user",
				"GET /api/auth/confirm":              "Confirm email address",
				"POST /api/auth/resend-verification": "Resend verification email",
				"POST /api/auth/forgot-password":     "Request password reset",
				"POST /api/auth/reset-password":      "Reset password",
				"POST /api/auth/account/confirm":     "Confirm account deletion",
				"GET /api/auth/me":                   "Get current user profile",
				"POST /api/auth/logout":              "Logout user",
				"PUT /api/auth/email":                "Update email address",
				"PUT /api/auth/username":             "Update username",
				"PUT /api/auth/password":             "Update password",
				"DELETE /api/auth/account":           "Request account deletion",
			},
			"posts": map[string]any{
				"GET /api/posts":         "List all posts",
				"GET /api/posts/search":  "Search posts",
				"GET /api/posts/title":   "Fetch title from URL",
				"GET /api/posts/{id}":    "Get specific post",
				"POST /api/posts":        "Create new post",
				"PUT /api/posts/{id}":    "Update post",
				"DELETE /api/posts/{id}": "Delete post",
			},
			"votes": map[string]any{
				"POST /api/posts/{id}/vote":   "Cast a vote",
				"DELETE /api/posts/{id}/vote": "Remove vote",
				"GET /api/posts/{id}/vote":    "Get user's vote",
				"GET /api/posts/{id}/votes":   "Get all votes for post",
			},
			"users": map[string]any{
				"GET /api/users":            "List all users",
				"POST /api/users":           "Create new user",
				"GET /api/users/{id}":       "Get specific user",
				"GET /api/users/{id}/posts": "Get user's posts",
			},
			"system": map[string]any{
				"GET /health":  "Health check",
				"GET /metrics": "Service metrics",
			},
		},
		"authentication": map[string]any{
			"type": "Bearer Token (JWT)",
			"note": "Include Authorization header with 'Bearer <token>' for protected endpoints",
		},
		"response_format": map[string]any{
			"success": "boolean",
			"message": "string",
			"data":    "object or array",
			"error":   "string (on error)",
		},
	}

	SendSuccessResponse(w, "API information retrieved successfully", apiInfo)
}

// @Summary Health check
// @Description Check the API health status along with database connectivity and SMTP service details
// @Tags api
// @Accept json
// @Produce json
// @Success 200 {object} CommonResponse "Health check successful"
// @Router /health [get]
func (h *APIHandler) GetHealth(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	if h.healthChecker != nil {
		health := h.healthChecker.CheckWithVersion(ctx, version.GetVersion())
		SendSuccessResponse(w, "Health check successful", health)
		return
	}

	currentTimestamp := time.Now().UTC().Format(time.RFC3339)

	health := map[string]any{
		"status":    "healthy",
		"timestamp": currentTimestamp,
		"version":   version.GetVersion(),
		"services": map[string]any{
			"database": "connected",
			"api":      "running",
		},
	}

	SendSuccessResponse(w, "Health check successful", health)
}

// @Summary Get metrics
// @Description Retrieve application metrics including aggregate counts and database performance data
// @Tags api
// @Accept json
// @Produce json
// @Success 200 {object} CommonResponse "Application metrics retrieved successfully"
// @Router /metrics [get]
func (h *APIHandler) GetMetrics(w http.ResponseWriter, r *http.Request) {

	postCount, err := h.postRepo.Count()
	if err != nil {
		SendErrorResponse(w, "Failed to get post count", http.StatusInternalServerError)
		return
	}

	userCount, err := h.userRepo.Count()
	if err != nil {
		SendErrorResponse(w, "Failed to get user count", http.StatusInternalServerError)
		return
	}

	totalVoteCount, _, err := h.voteService.GetVoteStatistics()
	if err != nil {
		SendErrorResponse(w, "Failed to get vote statistics", http.StatusInternalServerError)
		return
	}

	topPosts, err := h.postRepo.GetTopPosts(5)
	if err != nil {
		SendErrorResponse(w, "Failed to get top posts", http.StatusInternalServerError)
		return
	}

	var avgVotesPerPost float64
	if postCount > 0 {
		avgVotesPerPost = float64(totalVoteCount) / float64(postCount)
	}

	var totalScore int
	for _, post := range topPosts {
		totalScore += post.Score
	}

	var avgScore float64
	if len(topPosts) > 0 {
		avgScore = float64(totalScore) / float64(len(topPosts))
	}

	metrics := map[string]any{
		"posts": map[string]any{
			"total_count":     postCount,
			"top_posts_count": len(topPosts),
			"total_score":     totalScore,
			"average_score":   avgScore,
		},
		"users": map[string]any{
			"total_count": userCount,
		},
		"votes": map[string]any{
			"total_count":      totalVoteCount,
			"average_per_post": avgVotesPerPost,
			"note":             "All votes are counted together",
		},
		"system": map[string]any{
			"timestamp": time.Now().UTC().Format(time.RFC3339),
			"version":   version.GetVersion(),
		},
	}

	if h.metricsCollector != nil {
		performanceMetrics := h.metricsCollector.GetMetrics()
		metrics["database"] = map[string]any{
			"total_queries":    performanceMetrics.DBStats.TotalQueries,
			"slow_queries":     performanceMetrics.DBStats.SlowQueries,
			"average_duration": performanceMetrics.DBStats.AverageDuration.String(),
			"max_duration":     performanceMetrics.DBStats.MaxDuration.String(),
			"error_count":      performanceMetrics.DBStats.ErrorCount,
			"last_query_time":  performanceMetrics.DBStats.LastQueryTime.Format(time.RFC3339),
		}
		metrics["performance"] = map[string]any{
			"request_count":    performanceMetrics.RequestCount,
			"average_response": performanceMetrics.AverageResponse.String(),
			"max_response":     performanceMetrics.MaxResponse.String(),
			"error_count":      performanceMetrics.ErrorCount,
		}
	}

	SendSuccessResponse(w, "Metrics retrieved successfully", metrics)
}

func (h *APIHandler) MountRoutes(r chi.Router, config RouteModuleConfig) {
	public := r
	if config.GeneralRateLimit != nil {
		public = config.GeneralRateLimit(r)
	}
	public.Get("/", h.GetAPIInfo)
}
