package handlers

import (
	"fmt"
	"strconv"
	"time"

	"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
	"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
	"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
	"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
	"github.com/gofiber/fiber/v2"
)

// CreateBet godoc
// @Summary Create a bet
// @Description Creates a bet
// @Tags bet
// @Accept json
// @Produce json
// @Param createBet body domain.CreateBetReq true "Creates bet"
// @Success 200 {object} domain.BetRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /bet [post]
func (h *Handler) CreateBet(c *fiber.Ctx) error {
	fmt.Printf("Calling leagues")
	// Get user_id from middleware
	userID := c.Locals("user_id").(int64)
	role := c.Locals("role").(domain.Role)

	var req domain.CreateBetReq
	if err := c.BodyParser(&req); err != nil {
		h.logger.Error("Failed to parse CreateBet request", "error", err)
		return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
	}

	valErrs, ok := h.validator.Validate(c, req)
	if !ok {
		return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
	}

	res, err := h.betSvc.PlaceBet(c.Context(), req, userID, role)

	if err != nil {
		h.logger.Error("PlaceBet failed", "error", err)
		switch err {
		case bet.ErrEventHasBeenRemoved, bet.ErrEventHasNotEnded, bet.ErrRawOddInvalid, wallet.ErrBalanceInsufficient:
			return fiber.NewError(fiber.StatusBadRequest, err.Error())
		}
		return fiber.NewError(fiber.StatusInternalServerError, "Unable to create bet")
	}

	return response.WriteJSON(c, fiber.StatusOK, "Bet Created", res, nil)

}

// RandomBet godoc
// @Summary Generate a random bet
// @Description Generate a random bet
// @Tags bet
// @Accept json
// @Produce json
// @Param createBet body domain.RandomBetReq true "Create Random bet"
// @Success 200 {object} domain.BetRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /random/bet [post]
func (h *Handler) RandomBet(c *fiber.Ctx) error {

	// Get user_id from middleware
	userID := c.Locals("user_id").(int64)
	// role := c.Locals("role").(domain.Role)

	leagueIDQuery, err := strconv.Atoi(c.Query("league_id"))
	if err != nil {
		h.logger.Error("invalid league id", "error", err)
		return response.WriteJSON(c, fiber.StatusBadRequest, "invalid league id", nil, nil)
	}

	sportIDQuery, err := strconv.Atoi(c.Query("sport_id"))
	if err != nil {
		h.logger.Error("invalid sport id", "error", err)
		return response.WriteJSON(c, fiber.StatusBadRequest, "invalid sport id", nil, nil)
	}

	firstStartTimeQuery := c.Query("first_start_time")
	lastStartTimeQuery := c.Query("last_start_time")

	leagueID := domain.ValidInt32{
		Value: int32(leagueIDQuery),
		Valid: leagueIDQuery != 0,
	}
	sportID := domain.ValidInt32{
		Value: int32(sportIDQuery),
		Valid: sportIDQuery != 0,
	}

	var firstStartTime domain.ValidTime
	if firstStartTimeQuery != "" {
		firstStartTimeParsed, err := time.Parse(time.RFC3339, firstStartTimeQuery)
		if err != nil {
			h.logger.Error("invalid start_time format", "error", err)
			return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid start_time format", nil, nil)
		}
		firstStartTime = domain.ValidTime{
			Value: firstStartTimeParsed,
			Valid: true,
		}
	}
	var lastStartTime domain.ValidTime
	if lastStartTimeQuery != "" {
		lastStartTimeParsed, err := time.Parse(time.RFC3339, lastStartTimeQuery)
		if err != nil {
			h.logger.Error("invalid start_time format", "error", err)
			return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid start_time format", nil, nil)
		}
		lastStartTime = domain.ValidTime{
			Value: lastStartTimeParsed,
			Valid: true,
		}
	}

	var req domain.RandomBetReq
	if err := c.BodyParser(&req); err != nil {
		h.logger.Error("Failed to parse RandomBet request", "error", err)
		return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
	}

	valErrs, ok := h.validator.Validate(c, req)
	if !ok {
		return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
	}

	var res domain.CreateBetRes
	for i := 0; i < int(req.NumberOfBets); i++ {
		res, err = h.betSvc.PlaceRandomBet(c.Context(), userID, req.BranchID, leagueID, sportID, firstStartTime, lastStartTime)

		if err != nil {
			h.logger.Error("Random Bet failed", "error", err)
			switch err {
			case bet.ErrNoEventsAvailable:
				return fiber.NewError(fiber.StatusBadRequest, "No events found")
			}
			return fiber.NewError(fiber.StatusInternalServerError, "Unable to create random bet")
		}
	}
	return response.WriteJSON(c, fiber.StatusOK, "Bet Created", res, nil)

}

// GetAllBet godoc
// @Summary Gets all bets
// @Description Gets all the bets
// @Tags bet
// @Accept json
// @Produce json
// @Success 200 {array} domain.BetRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /bet [get]
func (h *Handler) GetAllBet(c *fiber.Ctx) error {
	companyID := c.Locals("company_id").(domain.ValidInt64)
	branchID := c.Locals("branch_id").(domain.ValidInt64)

	bets, err := h.betSvc.GetAllBets(c.Context(), domain.BetFilter{
		BranchID:  branchID,
		CompanyID: companyID,
	})
	if err != nil {
		h.logger.Error("Failed to get bets", "error", err)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve bets")
	}

	res := make([]domain.BetRes, len(bets))
	for i, bet := range bets {
		res[i] = domain.ConvertBet(bet)
	}

	return response.WriteJSON(c, fiber.StatusOK, "All bets retrieved successfully", res, nil)
}

// GetBetByID godoc
// @Summary Gets bet by id
// @Description Gets a single bet by id
// @Tags bet
// @Accept json
// @Produce json
// @Param id path int true "Bet ID"
// @Success 200 {object} domain.BetRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /bet/{id} [get]
func (h *Handler) GetBetByID(c *fiber.Ctx) error {
	betID := c.Params("id")
	id, err := strconv.ParseInt(betID, 10, 64)
	if err != nil {
		h.logger.Error("Invalid bet ID", "betID", betID, "error", err)
		return fiber.NewError(fiber.StatusBadRequest, "Invalid bet ID")
	}

	bet, err := h.betSvc.GetBetByID(c.Context(), id)
	if err != nil {
		// TODO: handle all the errors types
		h.logger.Error("Failed to get bet by ID", "betID", id, "error", err)
		return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet")
	}

	res := domain.ConvertBet(bet)

	return response.WriteJSON(c, fiber.StatusOK, "Bet retrieved successfully", res, nil)

}

// GetBetByCashoutID godoc
// @Summary Gets bet by cashout id
// @Description Gets a single bet by cashout id
// @Tags bet
// @Accept json
// @Produce json
// @Param id path string true "cashout ID"
// @Success 200 {object} domain.BetRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /bet/cashout/{id} [get]
func (h *Handler) GetBetByCashoutID(c *fiber.Ctx) error {
	cashoutID := c.Params("id")
	// id, err := strconv.ParseInt(cashoutID, 10, 64)

	// if err != nil {
	// 	logger.Error("Invalid cashout ID", "cashoutID", cashoutID, "error", err)
	// 	return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid cashout ID", err, nil)
	// }

	bet, err := h.betSvc.GetBetByCashoutID(c.Context(), cashoutID)
	if err != nil {
		h.logger.Error("Failed to get bet by ID", "cashoutID", cashoutID, "error", err)
		return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve bet", err, nil)
	}

	res := domain.ConvertBet(bet)

	return response.WriteJSON(c, fiber.StatusOK, "Bet retrieved successfully", res, nil)

}

type UpdateCashOutReq struct {
	CashedOut bool
}

// UpdateCashOut godoc
// @Summary Updates the cashed out field
// @Description Updates the cashed out field
// @Tags bet
// @Accept json
// @Produce json
// @Param id path int true "Bet ID"
// @Param updateCashOut body UpdateCashOutReq true "Updates Cashed Out"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /bet/{id} [patch]
func (h *Handler) UpdateCashOut(c *fiber.Ctx) error {
	type UpdateCashOutReq struct {
		CashedOut bool `json:"cashed_out" validate:"required" example:"true"`
	}

	betID := c.Params("id")
	id, err := strconv.ParseInt(betID, 10, 64)
	if err != nil {
		h.logger.Error("Invalid bet ID", "betID", betID, "error", err)
		return fiber.NewError(fiber.StatusBadRequest, "Invalid bet ID")
	}

	var req UpdateCashOutReq
	if err := c.BodyParser(&req); err != nil {
		h.logger.Error("Failed to parse UpdateCashOut request", "error", err)
		return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request body", err, nil)
	}

	if valErrs, ok := h.validator.Validate(c, req); !ok {
		return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
	}

	err = h.betSvc.UpdateCashOut(c.Context(), id, req.CashedOut)
	if err != nil {
		h.logger.Error("Failed to update cash out bet", "betID", id, "error", err)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to update cash out bet")
	}

	return response.WriteJSON(c, fiber.StatusOK, "Bet updated successfully", nil, nil)
}

// DeleteBet godoc
// @Summary Deletes bet by id
// @Description Deletes bet by id
// @Tags bet
// @Accept json
// @Produce json
// @Param id path int true "Bet ID"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /bet/{id} [delete]
func (h *Handler) DeleteBet(c *fiber.Ctx) error {
	betID := c.Params("id")
	id, err := strconv.ParseInt(betID, 10, 64)
	if err != nil {
		h.logger.Error("Invalid bet ID", "betID", betID, "error", err)
		return fiber.NewError(fiber.StatusBadRequest, "Invalid bet ID")
	}

	err = h.betSvc.DeleteBet(c.Context(), id)
	if err != nil {
		h.logger.Error("Failed to delete bet by ID", "betID", id, "error", err)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete bet")
	}

	return response.WriteJSON(c, fiber.StatusOK, "Bet removed successfully", nil, nil)
}
