package handlers

import (
	"log/slog"
	"strconv"
	"time"

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

type TransactionRes struct {
	ID               int64                `json:"id" example:"1"`
	Amount           float32              `json:"amount" example:"100.0"`
	BranchID         int64                `json:"branch_id" example:"1"`
	BranchName       string               `json:"branch_name" example:"Branch Name"`
	BranchLocation   string               `json:"branch_location" example:"Branch Location"`
	CompanyID        int64                `json:"company_id" example:"1"`
	CashierID        int64                `json:"cashier_id" example:"1"`
	CashierName      string               `json:"cashier_name" example:"John Smith"`
	BetID            int64                `json:"bet_id" example:"1"`
	NumberOfOutcomes int64                `json:"number_of_outcomes" example:"1"`
	Type             int64                `json:"type" example:"1"`
	PaymentOption    domain.PaymentOption `json:"payment_option" example:"1"`
	FullName         string               `json:"full_name" example:"John Smith"`
	PhoneNumber      string               `json:"phone_number" example:"0911111111"`
	BankCode         string               `json:"bank_code"`
	BeneficiaryName  string               `json:"beneficiary_name"`
	AccountName      string               `json:"account_name"`
	AccountNumber    string               `json:"account_number"`
	ReferenceNumber  string               `json:"reference_number"`
	Verified         bool                 `json:"verified" example:"true"`
	ApprovedBy       *int64               `json:"approved_by" example:"1"`
	ApproverName     *string              `json:"approver_name" example:"John Smith"`
	UpdatedAt        time.Time            `json:"updated_at"`
	CreatedAt        time.Time            `json:"created_at"`
}

type CreateTransactionReq struct {
	CashoutID       string               `json:"cashout_id" example:"191212"`
	Amount          float32              `json:"amount" example:"100.0"`
	BetID           int64                `json:"bet_id" example:"1"`
	Type            int64                `json:"type" example:"1"`
	PaymentOption   domain.PaymentOption `json:"payment_option" example:"1"`
	FullName        string               `json:"full_name" example:"John Smith"`
	PhoneNumber     string               `json:"phone_number" example:"0911111111"`
	BankCode        string               `json:"bank_code"`
	BeneficiaryName string               `json:"beneficiary_name"`
	AccountName     string               `json:"account_name"`
	AccountNumber   string               `json:"account_number"`
	ReferenceNumber string               `json:"reference_number"`
	BranchID        *int64               `json:"branch_id,omitempty" example:"1"`
}

func convertTransaction(transaction domain.Transaction) TransactionRes {
	newTransaction := TransactionRes{
		ID:               transaction.ID,
		Amount:           transaction.Amount.Float32(),
		BranchID:         transaction.BranchID,
		BranchName:       transaction.BranchName,
		BranchLocation:   transaction.BranchLocation,
		CompanyID:        transaction.CompanyID,
		CashierID:        transaction.CashierID,
		CashierName:      transaction.CashierName,
		BetID:            transaction.BetID,
		Type:             int64(transaction.Type),
		PaymentOption:    transaction.PaymentOption,
		FullName:         transaction.FullName,
		PhoneNumber:      transaction.PhoneNumber,
		BankCode:         transaction.BankCode,
		BeneficiaryName:  transaction.BeneficiaryName,
		AccountName:      transaction.AccountName,
		AccountNumber:    transaction.AccountNumber,
		ReferenceNumber:  transaction.ReferenceNumber,
		Verified:         transaction.Verified,
		NumberOfOutcomes: transaction.NumberOfOutcomes,
		CreatedAt:        transaction.CreatedAt,
		UpdatedAt:        transaction.UpdatedAt,
	}

	if transaction.ApprovedBy.Valid {
		newTransaction.ApprovedBy = &transaction.ApprovedBy.Value
		newTransaction.ApproverName = &transaction.ApproverName.Value

	}

	return newTransaction
}

// CreateTransaction godoc
// @Summary Create a transaction
// @Description Creates a transaction
// @Tags transaction
// @Accept json
// @Produce json
// @Param createBet body CreateTransactionReq true "Creates transaction"
// @Success 200 {object} TransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transaction [post]
func (h *Handler) CreateTransaction(c *fiber.Ctx) error {
	userID := c.Locals("user_id").(int64)
	role := c.Locals("role").(domain.Role)
	// user, err := h.userSvc.GetUserByID(c.Context(), userID)

	// TODO: Make a "Only Company" middleware auth and move this into that
	if role == domain.RoleCustomer {
		h.logger.Error("CreateTransactionReq failed due to unauthorized access")
		return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
			"error": "unauthorized access",
		})
	}

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

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

	var branchID int64
	var branchName string
	var branchLocation string
	var companyID int64
	if role == domain.RoleAdmin || role == domain.RoleBranchManager || role == domain.RoleSuperAdmin {
		if req.BranchID == nil {
			h.logger.Error("CreateTransactionReq Branch ID is required for this user role")
			return response.WriteJSON(c, fiber.StatusBadRequest, "Branch ID is required for this user role", nil, nil)
		}
		branch, err := h.branchSvc.GetBranchByID(c.Context(), *req.BranchID)
		if err != nil {
			h.logger.Error("CreateTransactionReq no branches")
			return response.WriteJSON(c, fiber.StatusBadRequest, "cannot find Branch ID", err, nil)
		}

		branchID = branch.ID
		branchName = branch.Name
		branchLocation = branch.Location
		companyID = branch.CompanyID
	} else {
		branch, err := h.branchSvc.GetBranchByCashier(c.Context(), userID)
		if err != nil {
			h.logger.Error("CreateTransactionReq failed, branch id invalid")
			return response.WriteJSON(c, fiber.StatusBadRequest, "Branch ID invalid", err, nil)
		}
		branchID = branch.ID
		branchName = branch.Name
		branchLocation = branch.Location
		companyID = branch.CompanyID
	}

	bet, err := h.betSvc.GetBetByID(c.Context(), req.BetID)
	if err != nil {
		h.logger.Error("CreateTransactionReq failed", "error", err)
		return response.WriteJSON(c, fiber.StatusBadRequest, "Bet ID invalid", err, nil)
	}

	// if bet.Status != domain.OUTCOME_STATUS_WIN {
	// 	h.logger.Error("CreateTransactionReq failed, bet has not won")
	// 	return response.WriteJSON(c, fiber.StatusBadRequest, "User has not won bet", err, nil)
	// }

	if bet.CashedOut {
		h.logger.Error(("Bet has already been cashed out"))
		return response.WriteJSON(c, fiber.StatusBadRequest, "This bet has already been cashed out", err, nil)
	}

	user, err := h.userSvc.GetUserByID(c.Context(), userID)
	if err != nil {
		h.logger.Error("CreateTransactionReq failed, user id invalid", "error", err)
		return response.WriteJSON(c, fiber.StatusBadRequest, "User ID invalid", err, nil)
	}
	transaction, err := h.transactionSvc.CreateTransaction(c.Context(), domain.CreateTransaction{
		BranchID:         branchID,
		CashierID:        userID,
		Amount:           domain.ToCurrency(req.Amount),
		BetID:            bet.ID,
		NumberOfOutcomes: int64(len(bet.Outcomes)),
		Type:             domain.TransactionType(req.Type),
		PaymentOption:    domain.PaymentOption(req.PaymentOption),
		FullName:         req.FullName,
		PhoneNumber:      req.PhoneNumber,
		BankCode:         req.BankCode,
		BeneficiaryName:  req.BeneficiaryName,
		AccountName:      req.AccountName,
		AccountNumber:    req.AccountNumber,
		ReferenceNumber:  req.ReferenceNumber,
		CashierName:      user.FirstName + " " + user.LastName,
		BranchName:       branchName,
		BranchLocation:   branchLocation,
		CompanyID:        companyID,
	})

	if err != nil {
		h.logger.Error("CreateTransactionReq failed", "error", err)
		return response.WriteJSON(c, fiber.StatusInternalServerError, "Internal Server Error", err, nil)
	}

	err = h.betSvc.UpdateCashOut(c.Context(), req.BetID, true)

	if err != nil {
		h.logger.Error("CreateTransactionReq failed", "error", err)
		return response.WriteJSON(c, fiber.StatusInternalServerError, "Internal Server Error", err, nil)
	}

	res := convertTransaction(transaction)
	return response.WriteJSON(c, fiber.StatusOK, "Transaction created successfully", res, nil)
}

// GetAllTransactions godoc
// @Summary Gets all transactions
// @Description Gets all the transactions
// @Tags transaction
// @Accept json
// @Produce json
// @Success 200 {array} TransactionRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transaction [get]
func (h *Handler) GetAllTransactions(c *fiber.Ctx) error {
	// Get user_id from middleware
	// userID := c.Locals("user_id").(int64)
	// role := c.Locals("role").(domain.Role)
	companyID := c.Locals("company_id").(domain.ValidInt64)
	branchID := c.Locals("branch_id").(domain.ValidInt64)

	var transactions []domain.Transaction

	// Check user role and fetch transactions accordingly
	transactions, err := h.transactionSvc.GetAllTransactions(c.Context(), domain.TransactionFilter{
		CompanyID: companyID,
		BranchID:  branchID,
	})

	if err != nil {
		h.logger.Error("Failed to get transactions", "error", err)
		return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve transactions", err, nil)
	}

	res := make([]TransactionRes, len(transactions))
	for i, transaction := range transactions {
		res[i] = convertTransaction(transaction)
	}

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

}

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

	transaction, err := h.transactionSvc.GetTransactionByID(c.Context(), id)
	if err != nil {
		h.logger.Error("Failed to get transaction by ID", "transactionID", id, "error", err)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve transaction")
	}

	res := convertTransaction(transaction)
	return response.WriteJSON(c, fiber.StatusOK, "Transaction retrieved successfully", res, nil)
}

type UpdateTransactionVerifiedReq struct {
	Verified bool `json:"verified" example:"true"`
}

// UpdateTransactionVerified godoc
// @Summary Updates the verified field of a transaction
// @Description Updates the verified status of a transaction
// @Tags transaction
// @Accept json
// @Produce json
// @Param id path int true "Transaction ID"
// @Param updateVerified body UpdateTransactionVerifiedReq true "Updates Transaction Verification"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transaction/{id} [put]
func (h *Handler) UpdateTransactionVerified(c *fiber.Ctx) error {
	transactionID := c.Params("id")
	userID := c.Locals("user_id").(int64)
	companyID := c.Locals("company_id").(domain.ValidInt64)
	role := c.Locals("role").(domain.Role)

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

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

	h.logger.Info("Update Transaction Verified", slog.Bool("verified", req.Verified))

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

	transaction, err := h.transactionSvc.GetTransactionByID(c.Context(), id)
	if role != domain.RoleSuperAdmin {
		if !companyID.Valid || companyID.Value != transaction.CompanyID {
			h.logger.Error("Failed to parse UpdateTransactionVerified request", "error", err)
			return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil)
		}
	}

	user, err := h.userSvc.GetUserById(c.Context(), userID)
	if err != nil {
		h.logger.Error("Invalid user ID", "userID", userID, "error", err)
		return fiber.NewError(fiber.StatusBadRequest, "Invalid user ID")
	}
	err = h.transactionSvc.UpdateTransactionVerified(c.Context(), id, req.Verified, userID, user.FirstName+" "+user.LastName)
	if err != nil {
		h.logger.Error("Failed to update transaction verification", "transactionID", id, "error", err)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to update transaction verification")
	}

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