package Astrologer

import (
	"fmt"
	"net/http"

	dto_astrologer "astrology-api/dto_astrologer"
	"astrology-api/helpers"
	services_astrologer "astrology-api/services_astrologer"

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

type DashboardController struct {
	service services_astrologer.DashboardService
}

func NewDashboardController(
	service services_astrologer.DashboardService,
) *DashboardController {

	return &DashboardController{
		service: service,
	}
}

///////////////////////////////////////////////////////////
// Dashboard
///////////////////////////////////////////////////////////

func (c *DashboardController) GetDashboard(ctx *gin.Context) {

	userID := ctx.MustGet("user_id").(uint)

	response, err := c.service.GetDashboard(userID)

	if err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	ctx.JSON(
		http.StatusOK,
		helpers.SuccessResponse(
			"Dashboard fetched successfully.",
			response,
		),
	)
}

///////////////////////////////////////////////////////////
// Dashboard Statistics
///////////////////////////////////////////////////////////

func (c *DashboardController) GetDashboardStatistics(ctx *gin.Context) {

	userID := ctx.MustGet("user_id").(uint)

	response, err := c.service.GetDashboardStatistics(userID)

	if err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	ctx.JSON(
		http.StatusOK,
		helpers.SuccessResponse(
			"Dashboard statistics fetched successfully.",
			response,
		),
	)
}

///////////////////////////////////////////////////////////
// Request Models
///////////////////////////////////////////////////////////

// Each flag is a *bool rather than a bool tagged `binding:"required"`. The
// validator counts false as the zero value, so "required" rejected the very
// request that turns a flag off — {"is_online": false} came back as
//
//	Key: 'OnlineStatusRequest.IsOnline' Error:Field validation for 'IsOnline'
//	failed on the 'required' tag
//
// The pointer distinguishes an absent key from an explicit false, and
// statusFlag below reports a missing one in plain words.

type OnlineStatusRequest struct {
	IsOnline *bool `json:"is_online"`
}

type ChatStatusRequest struct {
	IsAvailable *bool `json:"is_available"`
}

type CallStatusRequest struct {
	IsAvailable *bool `json:"is_available"`
}

type BusyStatusRequest struct {
	IsBusy *bool `json:"is_busy"`
}

type QuickDNDRequest struct {
	Enable *bool `json:"enable"`
}

// statusFlag unwraps one of those pointers, naming the JSON key when it is
// missing so the message says which field the client left out.
func statusFlag(value *bool, field string) (bool, error) {

	if value == nil {
		return false, fmt.Errorf("%s is required and must be true or false", field)
	}

	return *value, nil
}

var (
	_ = dto_astrologer.RestTimeRequest{}
)

// Update Online Status
func (c *DashboardController) UpdateOnlineStatus(ctx *gin.Context) {

	userID := ctx.MustGet("user_id").(uint)
	var req OnlineStatusRequest
	if err := ctx.ShouldBindJSON(&req); err != nil {
		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)
		return
	}

	isOnline, err := statusFlag(req.IsOnline, "is_online")

	if err != nil {
		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)
		return
	}

	status, err := c.service.UpdateOnlineStatus(userID, isOnline)
	if err != nil {
		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)
		return
	}

	ctx.JSON(
		http.StatusOK,
		helpers.SuccessResponse(
			"Online status updated successfully.",
			status,
		),
	)
}

///////////////////////////////////////////////////////////
// Update Chat Status
///////////////////////////////////////////////////////////

func (c *DashboardController) UpdateChatStatus(ctx *gin.Context) {

	userID := ctx.MustGet("user_id").(uint)

	var req ChatStatusRequest

	if err := ctx.ShouldBindJSON(&req); err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	isAvailable, err := statusFlag(req.IsAvailable, "is_available")

	if err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	status, err := c.service.UpdateChatStatus(
		userID,
		isAvailable,
	)

	if err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	ctx.JSON(
		http.StatusOK,
		helpers.SuccessResponse(
			"Chat status updated successfully.",
			status,
		),
	)
}

///////////////////////////////////////////////////////////
// Update Call Status
///////////////////////////////////////////////////////////

func (c *DashboardController) UpdateCallStatus(ctx *gin.Context) {

	userID := ctx.MustGet("user_id").(uint)

	var req CallStatusRequest

	if err := ctx.ShouldBindJSON(&req); err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	isAvailable, err := statusFlag(req.IsAvailable, "is_available")

	if err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	status, err := c.service.UpdateCallStatus(
		userID,
		isAvailable,
	)

	if err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	ctx.JSON(
		http.StatusOK,
		helpers.SuccessResponse(
			"Call status updated successfully.",
			status,
		),
	)
}

///////////////////////////////////////////////////////////
// Update Busy Status
///////////////////////////////////////////////////////////

func (c *DashboardController) UpdateBusyStatus(ctx *gin.Context) {

	userID := ctx.MustGet("user_id").(uint)

	var req BusyStatusRequest

	if err := ctx.ShouldBindJSON(&req); err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	isBusy, err := statusFlag(req.IsBusy, "is_busy")

	if err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	status, err := c.service.UpdateBusyStatus(
		userID,
		isBusy,
	)

	if err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	ctx.JSON(
		http.StatusOK,
		helpers.SuccessResponse(
			"Busy status updated successfully.",
			status,
		),
	)
}

///////////////////////////////////////////////////////////
// Update Quick DND
///////////////////////////////////////////////////////////

func (c *DashboardController) UpdateQuickDND(ctx *gin.Context) {

	userID := ctx.MustGet("user_id").(uint)

	var req QuickDNDRequest

	if err := ctx.ShouldBindJSON(&req); err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	enable, err := statusFlag(req.Enable, "enable")

	if err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	status, err := c.service.UpdateQuickDND(
		userID,
		enable,
	)

	if err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	ctx.JSON(
		http.StatusOK,
		helpers.SuccessResponse(
			"Quick DND updated successfully.",
			status,
		),
	)
}

///////////////////////////////////////////////////////////
// Start Rest Time
///////////////////////////////////////////////////////////

func (c *DashboardController) StartRestTime(ctx *gin.Context) {

	userID := ctx.MustGet("user_id").(uint)

	var req dto_astrologer.RestTimeRequest

	if err := ctx.ShouldBindJSON(&req); err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	restTime, err := c.service.StartRestTime(
		userID,
		req,
	)

	if err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	ctx.JSON(
		http.StatusOK,
		helpers.SuccessResponse(
			"Rest time started successfully.",
			restTime,
		),
	)
}

///////////////////////////////////////////////////////////
// End Rest Time
///////////////////////////////////////////////////////////

func (c *DashboardController) EndRestTime(ctx *gin.Context) {

	userID := ctx.MustGet("user_id").(uint)

	restTime, err := c.service.EndRestTime(userID)

	if err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	ctx.JSON(
		http.StatusOK,
		helpers.SuccessResponse(
			"Rest time ended successfully.",
			restTime,
		),
	)
}

///////////////////////////////////////////////////////////
// Update Offer
///////////////////////////////////////////////////////////

func (c *DashboardController) UpdateOffer(ctx *gin.Context) {

	userID := ctx.MustGet("user_id").(uint)

	var req dto_astrologer.OfferRequest

	if err := ctx.ShouldBindJSON(&req); err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	err := c.service.UpdateOffer(
		userID,
		req,
	)

	if err != nil {

		ctx.JSON(
			http.StatusBadRequest,
			helpers.ErrorResponse(err.Error()),
		)

		return
	}

	ctx.JSON(
		http.StatusOK,
		helpers.SuccessResponse(
			"Offer settings updated successfully.",
			nil,
		),
	)
}
