package drive

import (
	"io"
	"net/http"
	"strconv"
	"strings"

	"github.com/go-chi/chi/v5"

	"github.com/ultisuite/ulti-backend/internal/api/apiresponse"
	"github.com/ultisuite/ulti-backend/internal/api/apivalidate"
)

func (h *Handler) PublicRoutes() chi.Router {
	r := chi.NewRouter()
	r.Get("/shares/{token}", h.GetPublicShare)
	r.Get("/shares/{token}/preview", h.PreviewPublicShare)
	r.Get("/shares/{token}/download/*", h.DownloadPublicShare)
	r.Put("/shares/{token}/files/*", h.UploadPublicShare)
	r.Post("/shares/{token}/folders/*", h.CreatePublicShareFolder)
	r.Delete("/shares/{token}/files/*", h.DeletePublicShareItem)
	r.Post("/shares/{token}/rename", h.RenamePublicShareItem)
	if h.publicOffice != nil {
		h.publicOffice.RegisterPublicShareRoutes(r)
	}
	return r
}

func publicSharePassword(r *http.Request) string {
	return strings.TrimSpace(r.URL.Query().Get("password"))
}

func (h *Handler) GetPublicShare(w http.ResponseWriter, r *http.Request) {
	token := strings.TrimSpace(chi.URLParam(r, "token"))
	if token == "" {
		apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(
			apivalidate.FieldDetail{Field: "token", Message: "required"},
		))
		return
	}
	path := strings.TrimSpace(r.URL.Query().Get("path"))
	if path == "" {
		path = "/"
	}

	view, err := h.svc.GetPublicShare(r.Context(), token, path, publicSharePassword(r))
	if err != nil {
		writeDriveError(w, r, err)
		return
	}
	apiresponse.WriteJSON(w, http.StatusOK, view)
}

func (h *Handler) DownloadPublicShare(w http.ResponseWriter, r *http.Request) {
	token := strings.TrimSpace(chi.URLParam(r, "token"))
	if token == "" {
		apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(
			apivalidate.FieldDetail{Field: "token", Message: "required"},
		))
		return
	}
	filePath := strings.TrimSpace(chi.URLParam(r, "*"))
	if filePath == "" {
		filePath = "/"
	}

	body, contentType, err := h.svc.DownloadPublicShare(r.Context(), token, filePath, publicSharePassword(r))
	if err != nil {
		writeDriveError(w, r, err)
		return
	}
	defer body.Close()

	w.Header().Set("Content-Type", contentType)
	_, _ = io.Copy(w, body)
}

func (h *Handler) PreviewPublicShare(w http.ResponseWriter, r *http.Request) {
	token := strings.TrimSpace(chi.URLParam(r, "token"))
	if token == "" {
		apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(
			apivalidate.FieldDetail{Field: "token", Message: "required"},
		))
		return
	}
	filePath := strings.TrimSpace(r.URL.Query().Get("path"))
	if filePath == "" {
		apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(
			apivalidate.FieldDetail{Field: "path", Message: "required"},
		))
		return
	}
	width, _ := strconv.Atoi(r.URL.Query().Get("w"))
	height, _ := strconv.Atoi(r.URL.Query().Get("h"))

	body, contentType, err := h.svc.PreviewPublicShare(r.Context(), token, filePath, publicSharePassword(r), width, height)
	if err != nil {
		writeDriveError(w, r, err)
		return
	}
	defer body.Close()

	if contentType != "" {
		w.Header().Set("Content-Type", contentType)
	}
	_, _ = io.Copy(w, body)
}

func (h *Handler) UploadPublicShare(w http.ResponseWriter, r *http.Request) {
	token := strings.TrimSpace(chi.URLParam(r, "token"))
	filePath := strings.TrimSpace(chi.URLParam(r, "*"))
	if token == "" || filePath == "" {
		apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(
			apivalidate.FieldDetail{Field: "path", Message: "required"},
		))
		return
	}
	if !strings.HasPrefix(filePath, "/") {
		filePath = "/" + filePath
	}
	if err := h.svc.UploadPublicShare(r.Context(), token, filePath, publicSharePassword(r), r.Body, r.Header.Get("Content-Type")); err != nil {
		writeDriveError(w, r, err)
		return
	}
	w.WriteHeader(http.StatusCreated)
}

func (h *Handler) CreatePublicShareFolder(w http.ResponseWriter, r *http.Request) {
	token := strings.TrimSpace(chi.URLParam(r, "token"))
	folderPath := strings.TrimSpace(chi.URLParam(r, "*"))
	if token == "" || folderPath == "" {
		apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(
			apivalidate.FieldDetail{Field: "path", Message: "required"},
		))
		return
	}
	if !strings.HasPrefix(folderPath, "/") {
		folderPath = "/" + folderPath
	}
	if err := h.svc.CreatePublicShareFolder(r.Context(), token, folderPath, publicSharePassword(r)); err != nil {
		writeDriveError(w, r, err)
		return
	}
	w.WriteHeader(http.StatusCreated)
}

func (h *Handler) DeletePublicShareItem(w http.ResponseWriter, r *http.Request) {
	token := strings.TrimSpace(chi.URLParam(r, "token"))
	filePath := strings.TrimSpace(chi.URLParam(r, "*"))
	if token == "" || filePath == "" {
		apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(
			apivalidate.FieldDetail{Field: "path", Message: "required"},
		))
		return
	}
	if !strings.HasPrefix(filePath, "/") {
		filePath = "/" + filePath
	}
	if err := h.svc.DeletePublicShareItem(r.Context(), token, filePath, publicSharePassword(r)); err != nil {
		writeDriveError(w, r, err)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

type publicRenameRequest struct {
	Path    string `json:"path"`
	NewName string `json:"new_name"`
}

func (h *Handler) RenamePublicShareItem(w http.ResponseWriter, r *http.Request) {
	token := strings.TrimSpace(chi.URLParam(r, "token"))
	if token == "" {
		apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(
			apivalidate.FieldDetail{Field: "token", Message: "required"},
		))
		return
	}
	var req publicRenameRequest
	if err := apivalidate.DecodeJSON(w, r, 32<<10, &req); err != nil {
		return
	}
	if strings.TrimSpace(req.Path) == "" || strings.TrimSpace(req.NewName) == "" {
		apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(
			apivalidate.FieldDetail{Field: "path", Message: "required"},
			apivalidate.FieldDetail{Field: "new_name", Message: "required"},
		))
		return
	}
	if err := h.svc.RenamePublicShareItem(r.Context(), token, req.Path, req.NewName, publicSharePassword(r)); err != nil {
		writeDriveError(w, r, err)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}
