package handlers

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

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

type CreateAdminReq struct {
	FirstName   string `json:"first_name" example:"John"`
	LastName    string `json:"last_name" example:"Doe"`
	Email       string `json:"email" example:"john.doe@example.com"`
	PhoneNumber string `json:"phone_number" example:"1234567890"`
	Password    string `json:"password" example:"password123"`
	CompanyID   *int64 `json:"company_id,omitempty" example:"1"`
}

// CreateAdmin godoc
// @Summary Create Admin
// @Description Create Admin
// @Tags admin
// @Accept json
// @Produce json
// @Param manger body CreateAdminReq true "Create admin"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /admin [post]
func (h *Handler) CreateAdmin(c *fiber.Ctx) error {
	var companyID domain.ValidInt64
	var req CreateAdminReq

	if err := c.BodyParser(&req); err != nil {
		h.logger.Error("RegisterUser 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)
	}

	// Admins can be created without company ids and can be assigned later
	if req.CompanyID == nil {
		companyID = domain.ValidInt64{
			Value: 0,
			Valid: false,
		}
	} else {
		_, err := h.companySvc.GetCompanyByID(c.Context(), *req.CompanyID)
		if err != nil {
			h.logger.Error("CreateAdmin company id is invalid", "error", err)
			return response.WriteJSON(c, fiber.StatusInternalServerError, "Company ID is invalid", nil, nil)
		}
		companyID = domain.ValidInt64{
			Value: *req.CompanyID,
			Valid: true,
		}
	}

	user := domain.CreateUserReq{
		FirstName:   req.FirstName,
		LastName:    req.LastName,
		Email:       req.Email,
		PhoneNumber: req.PhoneNumber,
		Password:    req.Password,
		Role:        string(domain.RoleAdmin),
		CompanyID:   companyID,
	}

	h.logger.Info("CreateAdmin", slog.Bool("company id", req.CompanyID == nil))
	newUser, err := h.userSvc.CreateUser(c.Context(), user, true)
	if err != nil {
		h.logger.Error("CreateAdmin failed", "error", err)
		return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to create admin", nil, nil)
	}

	if req.CompanyID != nil {
		_, err := h.companySvc.UpdateCompany(c.Context(), domain.UpdateCompany{
			ID:      *req.CompanyID,
			AdminID: &newUser.ID,
		})
		if err != nil {
			h.logger.Error("CreateAdmin failed to update company", "error", err)
			return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to update company", nil, nil)
		}
	}

	return response.WriteJSON(c, fiber.StatusOK, "Admin created successfully", nil, nil)
}

type AdminRes struct {
	ID            int64       `json:"id"`
	FirstName     string      `json:"first_name"`
	LastName      string      `json:"last_name"`
	Email         string      `json:"email"`
	PhoneNumber   string      `json:"phone_number"`
	Role          domain.Role `json:"role"`
	EmailVerified bool        `json:"email_verified"`
	PhoneVerified bool        `json:"phone_verified"`
	CreatedAt     time.Time   `json:"created_at"`
	UpdatedAt     time.Time   `json:"updated_at"`
	LastLogin     time.Time   `json:"last_login"`
	SuspendedAt   time.Time   `json:"suspended_at"`
	Suspended     bool        `json:"suspended"`
}

// GetAllAdmins godoc
// @Summary Get all Admins
// @Description Get all Admins
// @Tags admin
// @Accept json
// @Produce json
// @Param page query int false "Page number"
// @Param page_size query int false "Page size"
// @Success 200 {object} AdminRes
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /admin [get]
func (h *Handler) GetAllAdmins(c *fiber.Ctx) error {

	filter := user.Filter{
		Role: string(domain.RoleAdmin),
		CompanyID: domain.ValidInt64{
			Value: int64(c.QueryInt("company_id")),
			Valid: false,
		},
		Page: domain.ValidInt{
			Value: c.QueryInt("page", 1) - 1,
			Valid: true,
		},
		PageSize: domain.ValidInt{
			Value: c.QueryInt("page_size", 10),
			Valid: true,
		},
	}
	valErrs, ok := h.validator.Validate(c, filter)
	if !ok {
		return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
	}
	admins, total, err := h.userSvc.GetAllUsers(c.Context(), filter)
	if err != nil {
		h.logger.Error("GetAllAdmins failed", "error", err)
		return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get Admins", nil, nil)
	}

	var result []AdminRes = make([]AdminRes, len(admins))
	for index, admin := range admins {
		lastLogin, err := h.authSvc.GetLastLogin(c.Context(), admin.ID)
		if err != nil {
			if err == authentication.ErrRefreshTokenNotFound {
				lastLogin = &admin.CreatedAt
			} else {
				h.logger.Error("Failed to get user last login", "userID", admin.ID, "error", err)
				return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login")
			}
		}
		result[index] = AdminRes{
			ID:            admin.ID,
			FirstName:     admin.FirstName,
			LastName:      admin.LastName,
			Email:         admin.Email,
			PhoneNumber:   admin.PhoneNumber,
			Role:          admin.Role,
			EmailVerified: admin.EmailVerified,
			PhoneVerified: admin.PhoneVerified,
			CreatedAt:     admin.CreatedAt,
			UpdatedAt:     admin.UpdatedAt,
			SuspendedAt:   admin.SuspendedAt,
			Suspended:     admin.Suspended,
			LastLogin:     *lastLogin,
		}
	}

	return response.WritePaginatedJSON(c, fiber.StatusOK, "Admins retrieved successfully", result, nil, filter.Page.Value, int(total))
}

// GetAdminByID godoc
// @Summary Get admin by id
// @Description Get a single admin by id
// @Tags admin
// @Accept json
// @Produce json
// @Param id path int true "User ID"
// @Success 200 {object} AdminRes
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /admin/{id} [get]
func (h *Handler) GetAdminByID(c *fiber.Ctx) error {
	// branchId := int64(12) //c.Locals("branch_id").(int64)
	// filter := user.Filter{
	// 	Role: string(domain.RoleUser),
	// 	BranchId: user.ValidBranchId{
	// 		Value: branchId,
	// 		Valid: true,
	// 	},
	// 	Page:     c.QueryInt("page", 1),
	// 	PageSize: c.QueryInt("page_size", 10),
	// }
	// valErrs, ok := validator.Validate(c, filter)
	// if !ok {
	// 	return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
	// }

	userIDstr := c.Params("id")
	userID, err := strconv.ParseInt(userIDstr, 10, 64)
	if err != nil {
		h.logger.Error("failed to fetch user using UserID", "error", err)
		return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid admin ID", nil, nil)
	}

	user, err := h.userSvc.GetUserByID(c.Context(), userID)
	if err != nil {
		h.logger.Error("Get User By ID failed", "error", err)
		return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get admin", nil, nil)
	}

	lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
	if err != nil {
		if err != authentication.ErrRefreshTokenNotFound {
			h.logger.Error("Failed to get user last login", "userID", user.ID, "error", err)
			return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login")
		}

		lastLogin = &user.CreatedAt
	}

	res := AdminRes{
		ID:            user.ID,
		FirstName:     user.FirstName,
		LastName:      user.LastName,
		Email:         user.Email,
		PhoneNumber:   user.PhoneNumber,
		Role:          user.Role,
		EmailVerified: user.EmailVerified,
		PhoneVerified: user.PhoneVerified,
		CreatedAt:     user.CreatedAt,
		UpdatedAt:     user.UpdatedAt,
		SuspendedAt:   user.SuspendedAt,
		Suspended:     user.Suspended,
		LastLogin:     *lastLogin,
	}

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

type updateAdminReq struct {
	FirstName string `json:"first_name" example:"John"`
	LastName  string `json:"last_name" example:"Doe"`
	Suspended bool   `json:"suspended" example:"false"`
	CompanyID *int64 `json:"company_id,omitempty" example:"1"`
}

// UpdateAdmin godoc
// @Summary Update Admin
// @Description Update Admin
// @Tags admin
// @Accept json
// @Produce json
// @Param admin body updateAdminReq true "Update Admin"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /admin/{id} [put]
func (h *Handler) UpdateAdmin(c *fiber.Ctx) error {
	var req updateAdminReq
	if err := c.BodyParser(&req); err != nil {
		h.logger.Error("UpdateAdmin failed", "error", err)
		return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", nil, nil)
	}

	valErrs, ok := h.validator.Validate(c, req)

	if !ok {
		return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
	}
	AdminIDStr := c.Params("id")
	AdminID, err := strconv.ParseInt(AdminIDStr, 10, 64)
	if err != nil {
		h.logger.Error("UpdateAdmin failed", "error", err)
		return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid Admin ID", nil, nil)
	}
	var companyID domain.ValidInt64
	if req.CompanyID != nil {
		companyID = domain.ValidInt64{
			Value: *req.CompanyID,
			Valid: true,
		}
	}
	err = h.userSvc.UpdateUser(c.Context(), domain.UpdateUserReq{
		UserId: AdminID,
		FirstName: domain.ValidString{
			Value: req.FirstName,
			Valid: req.FirstName != "",
		},
		LastName: domain.ValidString{
			Value: req.LastName,
			Valid: req.LastName != "",
		},
		Suspended: domain.ValidBool{
			Value: req.Suspended,
			Valid: true,
		},
		CompanyID: companyID,
	},
	)
	if err != nil {
		h.logger.Error("UpdateAdmin failed", "error", err)
		return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to update admin", nil, nil)
	}
	if req.CompanyID != nil {
		_, err := h.companySvc.UpdateCompany(c.Context(), domain.UpdateCompany{
			ID:      *req.CompanyID,
			AdminID: &AdminID,
		})
		if err != nil {
			h.logger.Error("CreateAdmin failed to update company", "error", err)
			return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to update company", nil, nil)
		}
	}

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

}
