package handlers

import (
	"strconv"
	"strings"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/google/uuid"
	"github.com/tapper/backend/database"
	"github.com/tapper/backend/models"
	"github.com/tapper/backend/services"
)

type createTicketRequest struct {
	Category string `json:"category"`
	Subject  string `json:"subject"`
	Body     string `json:"body"`
}

type addMessageRequest struct {
	Content string `json:"content"`
}

func CreateTicket(c *fiber.Ctx) error {
	userID := c.Locals("user_id").(uuid.UUID)
	tier := c.Locals("tier").(string)

	var req createTicketRequest
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Invalid request body",
		})
	}

	req.Category = strings.TrimSpace(req.Category)
	req.Subject = strings.TrimSpace(req.Subject)
	req.Body = strings.TrimSpace(req.Body)

	validCategories := map[string]bool{
		models.TicketCategoryBugReport:      true,
		models.TicketCategoryAccountIssue:   true,
		models.TicketCategoryBanAppeal:      true,
		models.TicketCategoryMatchIssue:     true,
		models.TicketCategoryFeatureRequest: true,
		models.TicketCategoryBilling:        true,
		models.TicketCategoryOther:          true,
	}
	if !validCategories[req.Category] {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Invalid ticket category",
		})
	}

	if len(req.Subject) < 5 || len(req.Subject) > 255 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Subject must be between 5 and 255 characters",
		})
	}
	if len(req.Body) < 10 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Message body must be at least 10 characters",
		})
	}

	ticket, err := services.CreateTicket(userID, req.Category, req.Subject, req.Body, tier)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Failed to create ticket",
		})
	}

	return c.Status(fiber.StatusCreated).JSON(ticket)
}

func ListOwnTickets(c *fiber.Ctx) error {
	userID := c.Locals("user_id").(uuid.UUID)

	page, _ := strconv.Atoi(c.Query("page", "1"))
	limit, _ := strconv.Atoi(c.Query("limit", "20"))
	if page < 1 {
		page = 1
	}
	if limit < 1 || limit > 50 {
		limit = 20
	}
	offset := (page - 1) * limit

	var total int64
	database.DB.Model(&models.Ticket{}).Where("user_id = ?", userID).Count(&total)

	var tickets []models.Ticket
	database.DB.Where("user_id = ?", userID).
		Order("updated_at DESC").
		Offset(offset).
		Limit(limit).
		Find(&tickets)

	return c.JSON(fiber.Map{
		"tickets": tickets,
		"total":   total,
		"page":    page,
		"limit":   limit,
	})
}

func GetTicketDetails(c *fiber.Ctx) error {
	userID := c.Locals("user_id").(uuid.UUID)
	ticketID, err := uuid.Parse(c.Params("id"))
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Invalid ticket ID",
		})
	}

	var ticket models.Ticket
	if err := database.DB.First(&ticket, ticketID).Error; err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
			"error": "Ticket not found",
		})
	}

	if ticket.UserID != userID {
		return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
			"error": "Access denied",
		})
	}

	var messages []models.TicketMessage
	database.DB.Where("ticket_id = ? AND is_internal = false", ticketID).
		Preload("Author").
		Order("created_at ASC").
		Find(&messages)

	return c.JSON(fiber.Map{
		"ticket":   ticket,
		"messages": messages,
	})
}

func AddTicketMessage(c *fiber.Ctx) error {
	userID := c.Locals("user_id").(uuid.UUID)
	ticketID, err := uuid.Parse(c.Params("id"))
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Invalid ticket ID",
		})
	}

	var req addMessageRequest
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Invalid request body",
		})
	}

	req.Content = strings.TrimSpace(req.Content)
	if len(req.Content) < 1 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Message content is required",
		})
	}

	var ticket models.Ticket
	if err := database.DB.First(&ticket, ticketID).Error; err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
			"error": "Ticket not found",
		})
	}
	if ticket.UserID != userID {
		return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
			"error": "Access denied",
		})
	}

	if ticket.Status == models.TicketStatusClosed {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Cannot add messages to a closed ticket",
		})
	}

	msg, err := services.AddTicketMessage(ticketID, userID, req.Content, false)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Failed to add message",
		})
	}

	if ticket.Status == models.TicketStatusWaitingOnUser || ticket.Status == models.TicketStatusResolved {
		now := time.Now().UTC()
		newStatus := models.TicketStatusReopened
		if ticket.Status == models.TicketStatusWaitingOnUser {
			newStatus = models.TicketStatusInProgress
		}
		database.DB.Model(&ticket).Updates(map[string]interface{}{
			"status":     newStatus,
			"updated_at": now,
		})
	}

	return c.Status(fiber.StatusCreated).JSON(msg)
}

func CloseOwnTicket(c *fiber.Ctx) error {
	userID := c.Locals("user_id").(uuid.UUID)
	ticketID, err := uuid.Parse(c.Params("id"))
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"error": "Invalid ticket ID",
		})
	}

	var ticket models.Ticket
	if err := database.DB.First(&ticket, ticketID).Error; err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
			"error": "Ticket not found",
		})
	}
	if ticket.UserID != userID {
		return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
			"error": "Access denied",
		})
	}

	if err := services.CloseTicket(ticketID); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": "Failed to close ticket",
		})
	}

	return c.JSON(fiber.Map{
		"message": "Ticket closed",
	})
}
