package controllers

import (
	"fotbal-club/internal/models"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

// PollControllerRefactored demonstrates refactored poll controller using utility helpers
// This is an example showing how to use the new utilities
type PollControllerRefactored struct {
	DB *gorm.DB
}

// NewPollControllerRefactored creates a new refactored poll controller
func NewPollControllerRefactored(db *gorm.DB) *PollControllerRefactored {
	return &PollControllerRefactored{DB: db}
}

// GetPolls returns paginated list of polls with filtering and search
// GET /api/v1/polls?search=election&active=true&sort=created_at:desc&page=1&page_size=20
func (pcr *PollControllerRefactored) GetPolls(c *gin.Context) {
	// Build query with search, filter, and sort
	query := QueryParser.BuildQueryChain(c, pcr.DB.Model(&models.Poll{})).
		WithSearch("title", "description").
		WithSort("created_at", "desc").
		WithBoolFilter("active", "active").
		Build()

	// Paginate and preload relationships
	var polls []models.Poll
	meta, err := Paginator.PaginateWithPreload(c, query, &polls, "Options")
	if err != nil {
		Respond.InternalError(c, "Failed to retrieve polls")
		return
	}

	Respond.SuccessWithMeta(c, polls, meta, "Polls retrieved successfully")
}

// GetPoll returns a single poll by ID
// GET /api/v1/polls/:id
func (pcr *PollControllerRefactored) GetPoll(c *gin.Context) {
	id := c.Param("id")

	var poll models.Poll
	if err := pcr.DB.Preload("Options").First(&poll, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			Respond.NotFound(c, "Poll not found")
			return
		}
		Respond.InternalError(c, "Failed to retrieve poll")
		return
	}

	Respond.Success(c, poll, "Poll retrieved successfully")
}

// CreatePoll creates a new poll with validation
// POST /api/v1/polls
func (pcr *PollControllerRefactored) CreatePoll(c *gin.Context) {
	type CreatePollRequest struct {
		Title       string   `json:"title" validate:"required,min=3,max=200"`
		Description string   `json:"description" validate:"omitempty,max=500"`
		Active      *bool    `json:"active"`
		MultiChoice *bool    `json:"multi_choice"`
		Options     []string `json:"options" validate:"required,min=2"`
	}

	var req CreatePollRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		Respond.BadRequest(c, "Invalid JSON: "+err.Error())
		return
	}

	// Validate request
	if !Validator.ValidateAndRespond(c, req) {
		return
	}

	// Sanitize inputs
	req.Title = Validator.SanitizeString(req.Title)
	req.Description = Validator.SanitizeString(req.Description)

	// Set defaults
	active := true
	if req.Active != nil {
		active = *req.Active
	}
	multiChoice := false
	if req.MultiChoice != nil {
		multiChoice = *req.MultiChoice
	}

	status := "draft"
	if active {
		status = "active"
	}

	maxChoices := 1
	if multiChoice {
		maxChoices = len(req.Options)
		if maxChoices == 0 {
			maxChoices = 1
		}
	}

	// Create poll
	poll := models.Poll{
		Title:         req.Title,
		Description:   req.Description,
		Status:        status,
		AllowMultiple: multiChoice,
		MaxChoices:    maxChoices,
	}

	if err := pcr.DB.Create(&poll).Error; err != nil {
		Respond.InternalError(c, "Failed to create poll")
		return
	}

	// Create options
	for _, optionText := range req.Options {
		option := models.PollOption{
			PollID: poll.ID,
			Text:   Validator.SanitizeString(optionText),
		}
		if err := pcr.DB.Create(&option).Error; err != nil {
			Respond.InternalError(c, "Failed to create poll option")
			return
		}
	}

	// Reload with options
	pcr.DB.Preload("Options").First(&poll, poll.ID)

	// Log creation
	if AuditLogger != nil {
		_ = AuditLogger.LogCreate(c, "Poll", poll.ID, "Poll created: "+poll.Title)
	}

	Respond.Created(c, poll, "Poll created successfully")
}

// UpdatePoll updates an existing poll
// PUT /api/v1/polls/:id
func (pcr *PollControllerRefactored) UpdatePoll(c *gin.Context) {
	id := c.Param("id")

	var poll models.Poll
	if err := pcr.DB.First(&poll, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			Respond.NotFound(c, "Poll not found")
			return
		}
		Respond.InternalError(c, "Failed to retrieve poll")
		return
	}

	// Store old values for audit
	oldTitle := poll.Title
	oldStatus := poll.Status

	type UpdatePollRequest struct {
		Title       string `json:"title" validate:"omitempty,min=3,max=200"`
		Description string `json:"description" validate:"omitempty,max=500"`
		Active      *bool  `json:"active"`
		MultiChoice *bool  `json:"multi_choice"`
	}

	var req UpdatePollRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		Respond.BadRequest(c, "Invalid JSON: "+err.Error())
		return
	}

	if !Validator.ValidateAndRespond(c, req) {
		return
	}

	// Apply updates
	if req.Title != "" {
		poll.Title = Validator.SanitizeString(req.Title)
	}
	if req.Description != "" {
		poll.Description = Validator.SanitizeString(req.Description)
	}
	if req.Active != nil {
		if *req.Active {
			poll.Status = "active"
		} else {
			poll.Status = "draft"
		}
	}
	if req.MultiChoice != nil {
		poll.AllowMultiple = *req.MultiChoice
		if poll.AllowMultiple && poll.MaxChoices < 1 {
			poll.MaxChoices = len(poll.Options)
			if poll.MaxChoices == 0 {
				poll.MaxChoices = 1
			}
		}
		if !poll.AllowMultiple {
			poll.MaxChoices = 1
		}
	}

	if err := pcr.DB.Save(&poll).Error; err != nil {
		Respond.InternalError(c, "Failed to update poll")
		return
	}

	// Log update
	if AuditLogger != nil {
		before := map[string]interface{}{
			"title":  oldTitle,
			"status": oldStatus,
		}
		after := map[string]interface{}{
			"title":  poll.Title,
			"status": poll.Status,
		}
		_ = AuditLogger.LogUpdate(c, "Poll", poll.ID, "Poll updated", before, after)
	}

	Respond.Success(c, poll, "Poll updated successfully")
}

// DeletePoll deletes a poll
// DELETE /api/v1/polls/:id
func (pcr *PollControllerRefactored) DeletePoll(c *gin.Context) {
	id := c.Param("id")

	var poll models.Poll
	if err := pcr.DB.First(&poll, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			Respond.NotFound(c, "Poll not found")
			return
		}
		Respond.InternalError(c, "Failed to retrieve poll")
		return
	}

	title := poll.Title
	pollID := poll.ID

	// Delete associated options first
	if err := pcr.DB.Where("poll_id = ?", poll.ID).Delete(&models.PollOption{}).Error; err != nil {
		Respond.InternalError(c, "Failed to delete poll options")
		return
	}

	// Delete poll
	if err := pcr.DB.Delete(&poll).Error; err != nil {
		Respond.InternalError(c, "Failed to delete poll")
		return
	}

	// Log deletion
	if AuditLogger != nil {
		_ = AuditLogger.LogDelete(c, "Poll", pollID, "Poll deleted: "+title)
	}

	Respond.NoContent(c)
}

// BatchDeletePolls deletes multiple polls
// POST /api/v1/polls/batch-delete
// Body: {"ids": [1, 2, 3]}
func (pcr *PollControllerRefactored) BatchDeletePolls(c *gin.Context) {
	if BatchOps == nil {
		BatchOps = NewBatchOperationsController(pcr.DB)
	}
	BatchOps.BatchDelete(c, &models.Poll{}, "polls")
}

// BatchActivatePolls activates multiple polls
// POST /api/v1/polls/batch-activate
// Body: {"ids": [1, 2, 3]}
func (pcr *PollControllerRefactored) BatchActivatePolls(c *gin.Context) {
	if BatchOps == nil {
		BatchOps = NewBatchOperationsController(pcr.DB)
	}
	BatchOps.BatchUpdate(c, &models.Poll{}, "polls", []string{"active"})
}

// VotePoll records a vote on a poll
// POST /api/v1/polls/:id/vote
// Body: {"option_id": 1} or {"option_ids": [1, 2]} for multi-choice
func (pcr *PollControllerRefactored) VotePoll(c *gin.Context) {
	id := c.Param("id")

	var poll models.Poll
	if err := pcr.DB.Preload("Options").First(&poll, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			Respond.NotFound(c, "Poll not found")
			return
		}
		Respond.InternalError(c, "Failed to retrieve poll")
		return
	}

	if !poll.IsActive() {
		Respond.BadRequest(c, "This poll is not active")
		return
	}

	var req struct {
		OptionID  *uint  `json:"option_id"`
		OptionIDs []uint `json:"option_ids"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		Respond.BadRequest(c, "Invalid JSON: "+err.Error())
		return
	}

	// Determine which options to vote for
	var optionIDs []uint
	if req.OptionID != nil {
		optionIDs = []uint{*req.OptionID}
	} else if len(req.OptionIDs) > 0 {
		optionIDs = req.OptionIDs
	} else {
		Respond.BadRequest(c, "No option selected")
		return
	}

	// Validate multi-choice
	if !poll.AllowMultiple && len(optionIDs) > 1 {
		Respond.BadRequest(c, "This poll does not allow multiple choices")
		return
	}

	if poll.AllowMultiple && poll.MaxChoices > 0 && len(optionIDs) > poll.MaxChoices {
		Respond.BadRequest(c, "You have selected too many options")
		return
	}

	// Get user ID (if authenticated)
	var userID *uint
	if user, exists := c.Get("user"); exists {
		if u, ok := user.(*models.User); ok && u != nil {
			userID = &u.ID
		}
	}

	// Check if already voted (if user is authenticated)
	if userID != nil {
		var existingVote models.PollVote
		if err := pcr.DB.Where("poll_id = ? AND user_id = ?", poll.ID, userID).First(&existingVote).Error; err == nil {
			Respond.Conflict(c, "You have already voted on this poll")
			return
		}
	}

	// Record votes
	for _, optionID := range optionIDs {
		vote := models.PollVote{
			PollID:   poll.ID,
			OptionID: optionID,
			UserID:   userID,
		}
		if err := pcr.DB.Create(&vote).Error; err != nil {
			Respond.InternalError(c, "Failed to record vote")
			return
		}
	}

	// Log vote
	if AuditLogger != nil {
		_ = AuditLogger.LogEntry(c, "VOTE", "Poll", &poll.ID, "Vote recorded on poll: "+poll.Title, nil)
	}

	Respond.Success(c, gin.H{"voted": true}, "Vote recorded successfully")
}

// GetPollResults returns poll results
// GET /api/v1/polls/:id/results
func (pcr *PollControllerRefactored) GetPollResults(c *gin.Context) {
	id := c.Param("id")

	var poll models.Poll
	if err := pcr.DB.Preload("Options").First(&poll, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			Respond.NotFound(c, "Poll not found")
			return
		}
		Respond.InternalError(c, "Failed to retrieve poll")
		return
	}

	// Count votes for each option
	type OptionResult struct {
		OptionID   uint   `json:"option_id"`
		OptionText string `json:"option_text"`
		VoteCount  int64  `json:"vote_count"`
	}

	results := make([]OptionResult, 0, len(poll.Options))
	var totalVotes int64

	for _, option := range poll.Options {
		var count int64
		pcr.DB.Model(&models.PollVote{}).Where("option_id = ?", option.ID).Count(&count)

		results = append(results, OptionResult{
			OptionID:   option.ID,
			OptionText: option.Text,
			VoteCount:  count,
		})

		totalVotes += count
	}

	response := gin.H{
		"poll_id":     poll.ID,
		"poll_title":  poll.Title,
		"total_votes": totalVotes,
		"results":     results,
	}

	Respond.Success(c, response, "Poll results retrieved successfully")
}
