package object_storage

import (
	"net/http"
	"time"

	"github.com/atlasos/calypso/internal/common/database"
	"github.com/atlasos/calypso/internal/common/logger"
	"github.com/gin-gonic/gin"
)

// Handler handles HTTP requests for object storage
type Handler struct {
	service      *Service
	setupService *SetupService
	logger       *logger.Logger
}

// NewHandler creates a new object storage handler
func NewHandler(service *Service, db *database.DB, log *logger.Logger) *Handler {
	return &Handler{
		service:      service,
		setupService: NewSetupService(db, log),
		logger:       log,
	}
}

// ListBuckets lists all buckets
func (h *Handler) ListBuckets(c *gin.Context) {
	buckets, err := h.service.ListBuckets(c.Request.Context())
	if err != nil {
		h.logger.Error("Failed to list buckets", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list buckets: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"buckets": buckets})
}

// GetBucket gets bucket information
func (h *Handler) GetBucket(c *gin.Context) {
	bucketName := c.Param("name")

	bucket, err := h.service.GetBucketStats(c.Request.Context(), bucketName)
	if err != nil {
		h.logger.Error("Failed to get bucket", "bucket", bucketName, "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get bucket: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, bucket)
}

// CreateBucketRequest represents a request to create a bucket
type CreateBucketRequest struct {
	Name string `json:"name" binding:"required"`
}

// CreateBucket creates a new bucket
func (h *Handler) CreateBucket(c *gin.Context) {
	var req CreateBucketRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid create bucket request", "error", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request: " + err.Error()})
		return
	}

	if err := h.service.CreateBucket(c.Request.Context(), req.Name); err != nil {
		h.logger.Error("Failed to create bucket", "bucket", req.Name, "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create bucket: " + err.Error()})
		return
	}

	c.JSON(http.StatusCreated, gin.H{"message": "bucket created successfully", "name": req.Name})
}

// DeleteBucket deletes a bucket
func (h *Handler) DeleteBucket(c *gin.Context) {
	bucketName := c.Param("name")

	if err := h.service.DeleteBucket(c.Request.Context(), bucketName); err != nil {
		h.logger.Error("Failed to delete bucket", "bucket", bucketName, "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete bucket: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "bucket deleted successfully"})
}

// GetAvailableDatasets gets all available pools and datasets for object storage setup
func (h *Handler) GetAvailableDatasets(c *gin.Context) {
	datasets, err := h.setupService.GetAvailableDatasets(c.Request.Context())
	if err != nil {
		h.logger.Error("Failed to get available datasets", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get available datasets: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"pools": datasets})
}

// SetupObjectStorageRequest represents a request to setup object storage
type SetupObjectStorageRequest struct {
	PoolName    string `json:"pool_name" binding:"required"`
	DatasetName string `json:"dataset_name" binding:"required"`
	CreateNew   bool   `json:"create_new"`
}

// SetupObjectStorage configures object storage with a ZFS dataset
func (h *Handler) SetupObjectStorage(c *gin.Context) {
	var req SetupObjectStorageRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid setup request", "error", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request: " + err.Error()})
		return
	}

	setupReq := SetupRequest{
		PoolName:    req.PoolName,
		DatasetName: req.DatasetName,
		CreateNew:   req.CreateNew,
	}

	result, err := h.setupService.SetupObjectStorage(c.Request.Context(), setupReq)
	if err != nil {
		h.logger.Error("Failed to setup object storage", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to setup object storage: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, result)
}

// GetCurrentSetup gets the current object storage configuration
func (h *Handler) GetCurrentSetup(c *gin.Context) {
	setup, err := h.setupService.GetCurrentSetup(c.Request.Context())
	if err != nil {
		h.logger.Error("Failed to get current setup", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get current setup: " + err.Error()})
		return
	}

	if setup == nil {
		c.JSON(http.StatusOK, gin.H{"configured": false})
		return
	}

	c.JSON(http.StatusOK, gin.H{"configured": true, "setup": setup})
}

// UpdateObjectStorage updates the object storage configuration
func (h *Handler) UpdateObjectStorage(c *gin.Context) {
	var req SetupObjectStorageRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid update request", "error", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request: " + err.Error()})
		return
	}

	setupReq := SetupRequest{
		PoolName:    req.PoolName,
		DatasetName: req.DatasetName,
		CreateNew:   req.CreateNew,
	}

	result, err := h.setupService.UpdateObjectStorage(c.Request.Context(), setupReq)
	if err != nil {
		h.logger.Error("Failed to update object storage", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update object storage: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, result)
}

// ListUsers lists all IAM users
func (h *Handler) ListUsers(c *gin.Context) {
	users, err := h.service.ListUsers(c.Request.Context())
	if err != nil {
		h.logger.Error("Failed to list users", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list users: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"users": users})
}

// CreateUserRequest represents a request to create a user
type CreateUserRequest struct {
	AccessKey string `json:"access_key" binding:"required"`
	SecretKey string `json:"secret_key" binding:"required"`
}

// CreateUser creates a new IAM user
func (h *Handler) CreateUser(c *gin.Context) {
	var req CreateUserRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid create user request", "error", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request: " + err.Error()})
		return
	}

	if err := h.service.CreateUser(c.Request.Context(), req.AccessKey, req.SecretKey); err != nil {
		h.logger.Error("Failed to create user", "access_key", req.AccessKey, "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create user: " + err.Error()})
		return
	}

	c.JSON(http.StatusCreated, gin.H{"message": "user created successfully", "access_key": req.AccessKey})
}

// DeleteUser deletes an IAM user
func (h *Handler) DeleteUser(c *gin.Context) {
	accessKey := c.Param("access_key")

	if err := h.service.DeleteUser(c.Request.Context(), accessKey); err != nil {
		h.logger.Error("Failed to delete user", "access_key", accessKey, "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete user: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "user deleted successfully"})
}

// ListServiceAccounts lists all service accounts (access keys)
func (h *Handler) ListServiceAccounts(c *gin.Context) {
	accounts, err := h.service.ListServiceAccounts(c.Request.Context())
	if err != nil {
		h.logger.Error("Failed to list service accounts", "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list service accounts: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"service_accounts": accounts})
}

// CreateServiceAccountRequest represents a request to create a service account
type CreateServiceAccountRequest struct {
	ParentUser string  `json:"parent_user" binding:"required"`
	Policy     string  `json:"policy,omitempty"`
	Expiration *string `json:"expiration,omitempty"` // ISO 8601 format
}

// CreateServiceAccount creates a new service account (access key)
func (h *Handler) CreateServiceAccount(c *gin.Context) {
	var req CreateServiceAccountRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Error("Invalid create service account request", "error", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request: " + err.Error()})
		return
	}

	var expiration *time.Time
	if req.Expiration != nil {
		exp, err := time.Parse(time.RFC3339, *req.Expiration)
		if err != nil {
			h.logger.Error("Invalid expiration format", "error", err)
			c.JSON(http.StatusBadRequest, gin.H{"error": "invalid expiration format, use ISO 8601 (RFC3339)"})
			return
		}
		expiration = &exp
	}

	account, err := h.service.CreateServiceAccount(c.Request.Context(), req.ParentUser, req.Policy, expiration)
	if err != nil {
		h.logger.Error("Failed to create service account", "parent_user", req.ParentUser, "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create service account: " + err.Error()})
		return
	}

	c.JSON(http.StatusCreated, account)
}

// DeleteServiceAccount deletes a service account
func (h *Handler) DeleteServiceAccount(c *gin.Context) {
	accessKey := c.Param("access_key")

	if err := h.service.DeleteServiceAccount(c.Request.Context(), accessKey); err != nil {
		h.logger.Error("Failed to delete service account", "access_key", accessKey, "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete service account: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "service account deleted successfully"})
}

// ListObjects lists objects in a bucket
func (h *Handler) ListObjects(c *gin.Context) {
	bucketName := c.Param("name")          // Changed from "bucket" to "name" to match route
	prefix := c.DefaultQuery("prefix", "") // Optional prefix (folder path)

	objects, err := h.service.ListObjects(c.Request.Context(), bucketName, prefix)
	if err != nil {
		h.logger.Error("Failed to list objects", "bucket", bucketName, "prefix", prefix, "error", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list objects: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"objects": objects})
}
