package handlers

import (
	"strconv"
	"time"

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

type TransferWalletRes struct {
	ID               int64     `json:"id" example:"1"`
	Amount           float32   `json:"amount" example:"100.0"`
	Verified         bool      `json:"verified" example:"true"`
	Type             string    `json:"type" example:"transfer"`
	PaymentMethod    string    `json:"payment_method" example:"bank"`
	ReceiverWalletID int64     `json:"receiver_wallet_id" example:"1"`
	SenderWalletID   *int64    `json:"sender_wallet_id" example:"1"`
	CashierID        *int64    `json:"cashier_id" example:"789"`
	CreatedAt        time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"`
	UpdatedAt        time.Time `json:"updated_at" example:"2025-04-08T12:30:00Z"`
}
type RefillRes struct {
	ID               int64     `json:"id" example:"1"`
	Amount           float32   `json:"amount" example:"100.0"`
	Verified         bool      `json:"verified" example:"true"`
	Type             string    `json:"type" example:"transfer"`
	PaymentMethod    string    `json:"payment_method" example:"bank"`
	ReceiverWalletID int64     `json:"receiver_wallet_id" example:"1"`
	SenderWalletID   *int64    `json:"sender_wallet_id" example:"1"`
	CashierID        *int64    `json:"cashier_id" example:"789"`
	CreatedAt        time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"`
	UpdatedAt        time.Time `json:"updated_at" example:"2025-04-08T12:30:00Z"`
}

func convertTransfer(transfer domain.Transfer) TransferWalletRes {
	var senderWalletID *int64
	senderWalletID = &transfer.SenderWalletID

	var cashierID *int64
	if transfer.CashierID.Valid {
		cashierID = &transfer.CashierID.Value
	}

	return TransferWalletRes{
		ID:               transfer.ID,
		Amount:           transfer.Amount.Float32(),
		Verified:         transfer.Verified,
		Type:             string(transfer.Type),
		PaymentMethod:    string(transfer.PaymentMethod),
		ReceiverWalletID: transfer.ReceiverWalletID,
		SenderWalletID:   senderWalletID,
		CashierID:        cashierID,
		CreatedAt:        transfer.CreatedAt,
		UpdatedAt:        transfer.UpdatedAt,
	}
}

type CreateTransferReq struct {
	Amount        float32 `json:"amount" example:"100.0"`
	PaymentMethod string  `json:"payment_method" example:"cash"`
}

type CreateRefillReq struct {
	Amount float32 `json:"amount" example:"100.0"`
}

// GetTransfersByWallet godoc
// @Summary Get transfer by  wallet
// @Description Get transfer by  wallet
// @Tags transfer
// @Accept json
// @Produce json
// @Param transferToWallet body CreateTransferReq true "Create Transfer"
// @Success 200 {object} TransferWalletRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transfer/wallet/{id} [get]
func (h *Handler) GetTransfersByWallet(c *fiber.Ctx) error {

	walletID := c.Params("id")

	id, err := strconv.ParseInt(walletID, 10, 64)

	if err != nil {
		h.logger.Error("Invalid wallet ID", "walletID", walletID, "error", err)
		return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid wallet ID", err, nil)
	}

	transfers, err := h.walletSvc.GetTransfersByWallet(c.Context(), int64(id))
	if err != nil {
		h.logger.Error("Failed to get transfers by wallet", "walletID", walletID, "error", err)
		return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve transfers", err, nil)
	}

	var transferResponses []TransferWalletRes
	for _, transfer := range transfers {
		transferResponses = append(transferResponses, convertTransfer(transfer))
	}

	return response.WriteJSON(c, fiber.StatusOK, "Transfers retrieved successfully", transferResponses, nil)

}

// TransferToWallet godoc
// @Summary Create a transfer to wallet
// @Description Create a transfer to wallet
// @Tags transfer
// @Accept json
// @Produce json
// @Param transferToWallet body CreateTransferReq true "Create Transfer"
// @Success 200 {object} TransferWalletRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transfer/wallet/:id [post]
func (h *Handler) TransferToWallet(c *fiber.Ctx) error {

	receiverIDString := c.Params("id")

	receiverID, err := strconv.ParseInt(receiverIDString, 10, 64)

	if err != nil {
		h.logger.Error("Invalid wallet ID", "walletID", receiverID, "error", err)
		return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid wallet ID", err, nil)
	}
	// Get sender ID from the cashier
	userID := c.Locals("user_id").(int64)
	role := string(c.Locals("role").(domain.Role))
	companyID := c.Locals("company_id").(int64)

	var senderID int64

	//TODO: check to make sure that the cashiers aren't transferring TO branch wallet
	if role == string(domain.RoleCustomer) {
		h.logger.Error("Unauthorized access", "userID", userID, "role", role)
		return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil)
	} else if role == string(domain.RoleBranchManager) || role == string(domain.RoleAdmin) || role == string(domain.RoleSuperAdmin) {
		company, err := h.companySvc.GetCompanyByID(c.Context(), companyID)
		if err != nil {
			return response.WriteJSON(c, fiber.StatusInternalServerError, "Error fetching company", err, nil)
		}
		senderID = company.WalletID
		h.logger.Error("Will", "userID", userID, "role", role)
	} else {
		cashierBranch, err := h.branchSvc.GetBranchByCashier(c.Context(), userID)
		if err != nil {
			h.logger.Error("Failed to get branch", "user ID", userID, "error", err)
			return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve cashier branch", err, nil)
		}
		senderID = cashierBranch.WalletID
	}

	var req CreateTransferReq

	if err := c.BodyParser(&req); err != nil {
		h.logger.Error("CreateTransferReq failed", "error", err)
		return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil)
	}

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

	transfer, err := h.walletSvc.TransferToWallet(c.Context(), senderID, receiverID, domain.ToCurrency(req.Amount), domain.PaymentMethod(req.PaymentMethod), domain.ValidInt64{Value: userID, Valid: true})

	if !ok {
		return response.WriteJSON(c, fiber.StatusInternalServerError, "Transfer Failed", err, nil)
	}

	res := convertTransfer(transfer)

	return response.WriteJSON(c, fiber.StatusOK, "Transfer Successful", res, nil)

}

// RefillWallet godoc
// @Summary Refill wallet
// @Description Super Admin route to refill a wallet
// @Tags transfer
// @Accept json
// @Produce json
// @Param refillWallet body CreateTransferReq true "Create Transfer"
// @Success 200 {object} TransferWalletRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transfer/refill/:id [post]
func (h *Handler) RefillWallet(c *fiber.Ctx) error {

	receiverIDString := c.Params("id")

	receiverID, err := strconv.ParseInt(receiverIDString, 10, 64)

	if err != nil {
		h.logger.Error("Invalid wallet ID", "walletID", receiverID, "error", err)
		return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid wallet ID", err, nil)
	}
	// Get sender ID from the cashier
	userID := c.Locals("user_id").(int64)
	role := string(c.Locals("role").(domain.Role))

	if role != string(domain.RoleSuperAdmin) {
		h.logger.Error("Unauthorized access", "userID", userID, "role", role)
		return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil)
	}

	var req CreateRefillReq

	if err := c.BodyParser(&req); err != nil {
		h.logger.Error("CreateRefillReq failed", "error", err)
		return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil)
	}

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

	transfer, err := h.walletSvc.RefillWallet(c.Context(), domain.CreateTransfer{
		Amount:           domain.ToCurrency(req.Amount),
		PaymentMethod:    domain.TRANSFER_BANK,
		ReceiverWalletID: receiverID,
		CashierID: domain.ValidInt64{
			Value: userID,
			Valid: true,
		},
		Type: domain.TransferType("deposit"),
	})

	if !ok {
		return response.WriteJSON(c, fiber.StatusInternalServerError, "Creating Transfer Failed", err, nil)
	}

	res := convertTransfer(transfer)

	return response.WriteJSON(c, fiber.StatusOK, "Transfer Successful", res, nil)

}
