package handlers

import (
	"Yimaru-Backend/internal/domain"
	"Yimaru-Backend/internal/services/authentication"
	"Yimaru-Backend/internal/web_server/response"
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"time"

	"github.com/gofiber/fiber/v2"
	"go.uber.org/zap"
)

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"`
}

// 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 /api/v1/admin [post]
func (h *Handler) CreateAdmin(c *fiber.Ctx) error {
	// var OrganizationID domain.ValidInt64
	var req CreateAdminReq

	if err := c.BodyParser(&req); err != nil {
		h.mongoLoggerSvc.Info("failed to parse CreateAdmin request",
			zap.Int64("status_code", fiber.StatusBadRequest),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusBadRequest, "Invalid request:"+err.Error())
	}

	valErrs, ok := h.validator.Validate(c, req)
	if !ok {
		var errMsg string
		for field, msg := range valErrs {
			errMsg += fmt.Sprintf("%s: %s; ", field, msg)
		}
		h.mongoLoggerSvc.Error("validation failed for CreateAdmin request",
			zap.Int64("status_code", fiber.StatusBadRequest),
			zap.Any("validation_errors", valErrs),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusBadRequest, errMsg)
	}

	// if req.OrganizationID == nil {
	// 	OrganizationID = domain.ValidInt64{
	// 		Value: 0,
	// 		Valid: false,
	// 	}
	// } else {
	// 	// _, err := h.companySvc.GetCompanyByID(c.Context(), *req.OrganizationID)
	// 	// if err != nil {
	// 	// 	h.mongoLoggerSvc.Error("invalid company ID for CreateAdmin",
	// 	// 		zap.Int64("status_code", fiber.StatusInternalServerError),
	// 	// 		zap.Int64("company_id", *req.OrganizationID),
	// 	// 		zap.Error(err),
	// 	// 		zap.Time("timestamp", time.Now()),
	// 	// 	)
	// 	// 	return fiber.NewError(fiber.StatusInternalServerError, "Company ID is invalid:"+err.Error())
	// 	// }
	// 	OrganizationID = domain.ValidInt64{
	// 		Value: *req.OrganizationID,
	// 		Valid: true,
	// 	}
	// }

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

	newUser, err := h.userSvc.CreateUser(c.Context(), user, true)
	if err != nil {
		h.mongoLoggerSvc.Error("failed to create admin user",
			zap.Int64("status_code", fiber.StatusInternalServerError),
			zap.Any("request", req),
			zap.Error(err),
			zap.Time("timestamp", time.Now()),
		)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to create admin:"+err.Error())
	}

	// if req.OrganizationID != nil {
	// 	_, err := h.companySvc.UpdateCompany(c.Context(), domain.UpdateCompany{
	// 		ID: *req.OrganizationID,
	// 		AdminID: domain.ValidInt64{
	// 			Value: newUser.ID,
	// 			Valid: true,
	// 		},
	// 	})
	// 	if err != nil {
	// 		h.mongoLoggerSvc.Error("failed to update company with new admin",
	// 			zap.Int64("status_code", fiber.StatusInternalServerError),
	// 			zap.Int64("company_id", *req.OrganizationID),
	// 			zap.Int64("admin_id", newUser.ID),
	// 			zap.Error(err),
	// 			zap.Time("timestamp", time.Now()),
	// 		)
	// 		return fiber.NewError(fiber.StatusInternalServerError, "Failed to update company"+err.Error())
	// 	}
	// }

	h.mongoLoggerSvc.Info("admin created successfully",
		zap.Int64("admin_id", newUser.ID),
		zap.String("email", newUser.Email),
		zap.Time("timestamp", time.Now()),
	)

	actorID := c.Locals("user_id").(int64)
	actorRole := string(c.Locals("role").(domain.Role))
	ip := c.IP()
	ua := c.Get("User-Agent")
	meta, _ := json.Marshal(map[string]interface{}{"email": req.Email, "admin_id": newUser.ID})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionAdminCreated, domain.ResourceAdmin, &newUser.ID, "Created admin: "+req.Email, meta, &ip, &ua)

	h.sendInAppNotification(newUser.ID, domain.NOTIFICATION_TYPE_ADMIN_CREATED, "Welcome to Yimaru", "Your admin account has been created successfully.")

	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 /api/v1/admin [get]
func (h *Handler) GetAllAdmins(c *fiber.Ctx) error {
	searchQuery := c.Query("query")
	searchString := domain.ValidString{
		Value: searchQuery,
		// Valid: searchQuery != "",
	}

	createdBeforeQuery := c.Query("created_before")
	var createdBefore domain.ValidTime
	if createdBeforeQuery != "" {
		parsed, err := time.Parse(time.RFC3339, createdBeforeQuery)
		if err != nil {
			h.logger.Info("invalid created_before format", "error", err)
			return fiber.NewError(fiber.StatusBadRequest, "Invalid created_before format")
		}
		createdBefore = domain.ValidTime{Value: parsed, Valid: true}
	}

	createdAfterQuery := c.Query("created_after")
	var createdAfter domain.ValidTime
	if createdAfterQuery != "" {
		parsed, err := time.Parse(time.RFC3339, createdAfterQuery)
		if err != nil {
			h.logger.Info("invalid created_after format", "error", err)
			return fiber.NewError(fiber.StatusBadRequest, "Invalid created_after format")
		}
		createdAfter = domain.ValidTime{Value: parsed, Valid: true}
	}

	// companyID := int64(c.QueryInt("company_id"))
	filter := domain.UserFilter{
		Role: string(domain.RoleAdmin),
		// OrganizationID: domain.ValidInt64{
		// 	Value: companyID,
		// 	Valid: companyID != 0,
		// },
		Page:          int64(c.QueryInt("page", 1) - 1),
		PageSize:      int64(c.QueryInt("page_size", 10)),
		Query:         searchString.Value,
		CreatedBefore: createdBefore,
		CreatedAfter:  createdAfter,
	}

	if valErrs, ok := h.validator.Validate(c, filter); !ok {
		var errMsg string
		for f, msg := range valErrs {
			errMsg += fmt.Sprintf("%s: %s; ", f, msg)
		}
		h.mongoLoggerSvc.Info("invalid filter values in GetAllAdmins request",
			zap.Int("status_code", fiber.StatusBadRequest),
			zap.Any("validation_errors", valErrs),
			zap.Time("timestamp", time.Now()))
		return fiber.NewError(fiber.StatusBadRequest, errMsg)
	}

	admins, total, err := h.userSvc.GetAllUsers(c.Context(), filter)
	if err != nil {
		h.mongoLoggerSvc.Error("failed to get admins",
			zap.Int("status_code", fiber.StatusInternalServerError),
			zap.Any("filter", filter),
			zap.Error(err),
			zap.Time("timestamp", time.Now()))
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to get admins: "+err.Error())
	}

	result := make([]AdminRes, len(admins))
	for i, admin := range admins {
		lastLogin, err := h.authSvc.GetLastLogin(c.Context(), admin.ID)
		if err != nil && err != authentication.ErrRefreshTokenNotFound {
			h.mongoLoggerSvc.Error("failed to get last login",
				zap.Int("status_code", fiber.StatusInternalServerError),
				zap.Int64("admin_id", admin.ID),
				zap.Error(err),
				zap.Time("timestamp", time.Now()))
			return fiber.NewError(fiber.StatusInternalServerError, "Failed to get last login: "+err.Error())
		}
		if err == authentication.ErrRefreshTokenNotFound {
			lastLogin = &admin.CreatedAt
		}

		result[i] = 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,
			LastLogin:     *lastLogin,
		}
	}

	h.mongoLoggerSvc.Info("admins retrieved successfully",
		zap.Int("status_code", fiber.StatusOK),
		zap.Int("count", len(result)),
		zap.Int("page", int(filter.Page+1)),
		zap.Time("timestamp", time.Now()),
	)

	return response.WritePaginatedJSON(c, fiber.StatusOK, "Admins retrieved successfully", result, nil, int(filter.Page+1), 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 /api/v1/admin/{id} [get]
func (h *Handler) GetAdminByID(c *fiber.Ctx) error {
	idStr := c.Params("id")
	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid admin ID")
	}

	user, err := h.userSvc.GetUserByID(c.Context(), id)
	if err != nil {
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to get admin: "+err.Error())
	}

	lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
	if err != nil && err != authentication.ErrRefreshTokenNotFound {
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to get last login: "+err.Error())
	}
	if err == authentication.ErrRefreshTokenNotFound {
		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,
		LastLogin:     *lastLogin,
	}

	return response.WriteJSON(c, fiber.StatusOK, "Admin 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"`
}

// 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 /api/v1/admin/{id} [put]
func (h *Handler) UpdateAdmin(c *fiber.Ctx) error {
	var req updateAdminReq
	if err := c.BodyParser(&req); err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid request body: "+err.Error())
	}

	adminIDStr := c.Params("id")
	adminID, err := strconv.ParseInt(adminIDStr, 10, 64)
	if err != nil {
		return fiber.NewError(fiber.StatusBadRequest, "Invalid admin ID")
	}

	// var orgID domain.ValidInt64
	// if req.OrganizationID != nil {
	// 	orgID = domain.ValidInt64{
	// 		Value: *req.OrganizationID,
	// 		Valid: true,
	// 	}
	// }

	err = h.userSvc.UpdateUser(c.Context(), domain.UpdateUserReq{
		UserID:    adminID,
		FirstName: req.FirstName,
		LastName:  req.LastName,
		// OrganizationID: orgID,
	})
	if err != nil {
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to update admin: "+err.Error())
	}

	actorID := c.Locals("user_id").(int64)
	actorRole := string(c.Locals("role").(domain.Role))
	ip := c.IP()
	ua := c.Get("User-Agent")
	meta, _ := json.Marshal(map[string]interface{}{"admin_id": adminID, "first_name": req.FirstName, "last_name": req.LastName})
	go h.activityLogSvc.RecordAction(context.Background(), &actorID, &actorRole, domain.ActionAdminUpdated, domain.ResourceAdmin, &adminID, fmt.Sprintf("Updated admin ID: %d", adminID), meta, &ip, &ua)

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