package Astrologer

import (
	"astrology-api/helpers"
	"astrology-api/services"
	"net/http"

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

///////////////////////////////////////////////////////////
// Consultation Controller
///////////////////////////////////////////////////////////
//
// The astrologer's side of the handshake: see who is ringing, accept or
// reject, and hang up.
//
// Every one of these calls the user-side consultation service on purpose.
// Accepting decides whether a customer's wallet is about to be spent and
// whether their one free chat is consumed, and billing touches the wallet, the
// platform fee and the settlement status. There must be exactly one
// implementation of all of that — an astrologer ending a call has to bill
// identically to the customer ending it.

type ConsultationController struct {
	service services.ConsultationService
}

func NewConsultationController(
	service services.ConsultationService,
) *ConsultationController {

	return &ConsultationController{
		service: service,
	}
}

type endConsultationRequest struct {
	ConsultationID uint `json:"consultation_id" binding:"required"`
}

// EndConsultation closes a session the astrologer is a party to and bills it.
// The earning lands at PENDING for admin review, exactly as it would have if
// the customer had hung up.
//
// POST /api/astrologer/consultation/end
func (c *ConsultationController) EndConsultation(ctx *gin.Context) {

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

	var request endConsultationRequest

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

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

		return
	}

	response, err := c.service.EndConsultationByAstrologer(
		userID,
		request.ConsultationID,
	)

	if err != nil {

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

		return
	}

	ctx.JSON(
		http.StatusOK,
		helpers.SuccessResponse(
			"Consultation ended successfully.",
			response,
		),
	)
}

///////////////////////////////////////////////////////////
// Incoming requests
///////////////////////////////////////////////////////////

// ListIncomingRequests is the ringing queue.
//
// The push is the fast path, but a data-only message to a dozing Android
// device can arrive after the request has already rung out, so the app polls
// this as well while the astrologer is online. Rows past their deadline are
// closed on read, so the queue never offers something that can no longer be
// accepted.
//
// GET /api/astrologer/consultation/requests
func (c *ConsultationController) ListIncomingRequests(ctx *gin.Context) {

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

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

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

	ctx.JSON(http.StatusOK, helpers.SuccessResponse("Consultation requests", response))
}

///////////////////////////////////////////////////////////
// Accept
///////////////////////////////////////////////////////////

// AcceptConsultation commits the astrologer to the session.
//
// It does not start the clock: the customer still has to open the session
// before anything is billed. What it does do is mark the astrologer busy,
// claim the customer's free chat if one is in play, and hand over the
// astrologer's own credentials.
//
// POST /api/astrologer/consultation/accept
func (c *ConsultationController) AcceptConsultation(ctx *gin.Context) {

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

	var request endConsultationRequest

	if err := ctx.ShouldBindJSON(&request); err != nil {
		ctx.JSON(http.StatusBadRequest, helpers.ErrorResponse(err.Error()))
		return
	}

	response, err := c.service.AcceptConsultation(userID, request.ConsultationID)

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

	ctx.JSON(http.StatusOK, helpers.SuccessResponse("Consultation accepted", response))
}

///////////////////////////////////////////////////////////
// Reject
///////////////////////////////////////////////////////////

type rejectConsultationRequest struct {
	ConsultationID uint `json:"consultation_id" binding:"required"`

	// Shown to the customer verbatim, so keep it short and civil.
	Reason string `json:"reason"`
}

// RejectConsultation declines a request. Nothing is billed and the customer's
// free chat, if one was in play, is untouched.
//
// POST /api/astrologer/consultation/reject
func (c *ConsultationController) RejectConsultation(ctx *gin.Context) {

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

	var request rejectConsultationRequest

	if err := ctx.ShouldBindJSON(&request); err != nil {
		ctx.JSON(http.StatusBadRequest, helpers.ErrorResponse(err.Error()))
		return
	}

	response, err := c.service.RejectConsultation(userID, request.ConsultationID, request.Reason)

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

	ctx.JSON(http.StatusOK, helpers.SuccessResponse("Consultation rejected", response))
}

///////////////////////////////////////////////////////////
// Active
///////////////////////////////////////////////////////////

// GetActiveConsultation recovers whatever the astrologer is currently in.
//
// Unlike the customer's equivalent this is READ-ONLY: it never bills or closes
// a stale session. The astrologer is not the party whose ticks define
// liveness, so a quiet astrologer app must not be able to close a session the
// customer is still sitting in.
//
// GET /api/astrologer/consultation/active
func (c *ConsultationController) GetActiveConsultation(ctx *gin.Context) {

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

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

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

	ctx.JSON(http.StatusOK, helpers.SuccessResponse("Active consultation", response))
}

///////////////////////////////////////////////////////////
// Agora
///////////////////////////////////////////////////////////

// GetAgoraToken mints a fresh token for a session the astrologer is a party
// to. Called from the SDK's token-expiring callback.
//
// POST /api/astrologer/consultation/agora-token
func (c *ConsultationController) GetAgoraToken(ctx *gin.Context) {

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

	var request endConsultationRequest

	if err := ctx.ShouldBindJSON(&request); err != nil {
		ctx.JSON(http.StatusBadRequest, helpers.ErrorResponse(err.Error()))
		return
	}

	response, err := c.service.AstrologerAgoraToken(userID, request.ConsultationID)

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

	ctx.JSON(http.StatusOK, helpers.SuccessResponse("Agora token", response))
}
