package handlers

import (
	"encoding/json"
	"fmt"
	"net/http"
	"path/filepath"
	"strings"
	"time"

	"github.com/rede5/gohorsejobs/backend/internal/utils/uuid"
)

// StorageServiceInterface defines the contract for storage operations
type StorageServiceInterface interface {
	GenerateUploadURL(key string, contentType string, expiryMinutes int) (string, error)
	GenerateDownloadURL(key string, expiryMinutes int) (string, error)
	GetPublicURL(key string) string
	DeleteObject(key string) error
}

// StorageHandler handles file storage operations
type StorageHandler struct {
	Storage StorageServiceInterface
}

// NewStorageHandler creates a new storage handler
func NewStorageHandler(s StorageServiceInterface) *StorageHandler {
	return &StorageHandler{Storage: s}
}

// UploadURLRequest represents a request for a pre-signed upload URL
type UploadURLRequest struct {
	Filename    string `json:"filename"`
	ContentType string `json:"contentType"`
	Folder      string `json:"folder"` // Optional: logos, resumes, documents
}

// UploadURLResponse represents the response with a pre-signed upload URL
type UploadURLResponse struct {
	UploadURL string `json:"uploadUrl"`
	Key       string `json:"key"`
	PublicURL string `json:"publicUrl"`
	ExpiresIn int    `json:"expiresIn"` // seconds
}

// DownloadURLRequest represents a request for a pre-signed download URL
type DownloadURLRequest struct {
	Key string `json:"key"`
}

// DownloadURLResponse represents the response with a pre-signed download URL
type DownloadURLResponse struct {
	DownloadURL string `json:"downloadUrl"`
	ExpiresIn   int    `json:"expiresIn"` // seconds
}

// GenerateUploadURL creates a pre-signed S3 URL for uploads
// @Summary      Generate upload URL
// @Description  Generate a pre-signed URL to upload a file to S3
// @Tags         Storage
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        request  body      UploadURLRequest  true  "Upload request"
// @Success      200      {object}  UploadURLResponse
// @Failure      400      {string}  string  "Bad Request"
// @Failure      500      {string}  string  "Internal Server Error"
// @Router       /api/v1/storage/upload-url [post]
func (h *StorageHandler) GenerateUploadURL(w http.ResponseWriter, r *http.Request) {
	var req UploadURLRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	if req.Filename == "" {
		http.Error(w, "Filename is required", http.StatusBadRequest)
		return
	}

	if req.ContentType == "" {
		// Try to infer from extension
		ext := strings.ToLower(filepath.Ext(req.Filename))
		switch ext {
		case ".jpg", ".jpeg":
			req.ContentType = "image/jpeg"
		case ".png":
			req.ContentType = "image/png"
		case ".gif":
			req.ContentType = "image/gif"
		case ".webp":
			req.ContentType = "image/webp"
		case ".pdf":
			req.ContentType = "application/pdf"
		case ".doc":
			req.ContentType = "application/msword"
		case ".docx":
			req.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
		default:
			req.ContentType = "application/octet-stream"
		}
	}

	// Validate folder
	folder := "uploads"
	if req.Folder != "" {
		validFolders := map[string]bool{
			"logos":     true,
			"resumes":   true,
			"documents": true,
			"avatars":   true,
		}
		if validFolders[req.Folder] {
			folder = req.Folder
		}
	}

	// Generate unique key
	ext := filepath.Ext(req.Filename)
	uniqueID := uuid.V7()
	timestamp := time.Now().Format("20060102")
	key := fmt.Sprintf("%s/%s/%s%s", folder, timestamp, uniqueID, ext)

	// Generate pre-signed URL (15 minutes expiry)
	expiryMinutes := 15
	uploadURL, err := h.Storage.GenerateUploadURL(key, req.ContentType, expiryMinutes)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to generate upload URL: %v", err), http.StatusInternalServerError)
		return
	}

	response := UploadURLResponse{
		UploadURL: uploadURL,
		Key:       key,
		PublicURL: h.Storage.GetPublicURL(key),
		ExpiresIn: expiryMinutes * 60,
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

// GenerateDownloadURL creates a pre-signed S3 URL for downloads
// @Summary      Generate download URL
// @Description  Generate a pre-signed URL to download a private file from S3
// @Tags         Storage
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        request  body      DownloadURLRequest  true  "Download request"
// @Success      200      {object}  DownloadURLResponse
// @Failure      400      {string}  string  "Bad Request"
// @Failure      500      {string}  string  "Internal Server Error"
// @Router       /api/v1/storage/download-url [post]
func (h *StorageHandler) GenerateDownloadURL(w http.ResponseWriter, r *http.Request) {
	var req DownloadURLRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	if req.Key == "" {
		http.Error(w, "Key is required", http.StatusBadRequest)
		return
	}

	// Generate pre-signed URL (1 hour expiry)
	expiryMinutes := 60
	downloadURL, err := h.Storage.GenerateDownloadURL(req.Key, expiryMinutes)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to generate download URL: %v", err), http.StatusInternalServerError)
		return
	}

	response := DownloadURLResponse{
		DownloadURL: downloadURL,
		ExpiresIn:   expiryMinutes * 60,
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

// DeleteFile removes an object from S3
// @Summary      Delete file
// @Description  Delete a stored file by key
// @Tags         Storage
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        key  query     string  true  "File key"
// @Success      204
// @Failure      400  {string}  string  "Bad Request"
// @Failure      500  {string}  string  "Internal Server Error"
// @Router       /api/v1/storage/files [delete]
func (h *StorageHandler) DeleteFile(w http.ResponseWriter, r *http.Request) {
	key := r.URL.Query().Get("key")
	if key == "" {
		http.Error(w, "Key query parameter is required", http.StatusBadRequest)
		return
	}

	if err := h.Storage.DeleteObject(key); err != nil {
		http.Error(w, fmt.Sprintf("Failed to delete file: %v", err), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}
