package handlers

import (
	"fmt"
	"io"
	"net/http"

	"mgit.msbls.de/m/KanzlAI-mGMT/internal/auth"
	"mgit.msbls.de/m/KanzlAI-mGMT/internal/services"

	"github.com/google/uuid"
)

const maxUploadSize = 50 << 20 // 50 MB

type DocumentHandler struct {
	svc *services.DocumentService
}

func NewDocumentHandler(svc *services.DocumentService) *DocumentHandler {
	return &DocumentHandler{svc: svc}
}

func (h *DocumentHandler) ListByCase(w http.ResponseWriter, r *http.Request) {
	tenantID, ok := auth.TenantFromContext(r.Context())
	if !ok {
		writeError(w, http.StatusForbidden, "missing tenant")
		return
	}

	caseID, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid case ID")
		return
	}

	docs, err := h.svc.ListByCase(r.Context(), tenantID, caseID)
	if err != nil {
		internalError(w, "failed to list documents", err)
		return
	}

	writeJSON(w, http.StatusOK, map[string]any{
		"documents": docs,
		"total":     len(docs),
	})
}

func (h *DocumentHandler) Upload(w http.ResponseWriter, r *http.Request) {
	tenantID, ok := auth.TenantFromContext(r.Context())
	if !ok {
		writeError(w, http.StatusForbidden, "missing tenant")
		return
	}
	userID, _ := auth.UserFromContext(r.Context())

	caseID, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid case ID")
		return
	}

	r.Body = http.MaxBytesReader(w, r.Body, maxUploadSize)
	if err := r.ParseMultipartForm(maxUploadSize); err != nil {
		writeError(w, http.StatusBadRequest, "file too large or invalid multipart form")
		return
	}

	file, header, err := r.FormFile("file")
	if err != nil {
		writeError(w, http.StatusBadRequest, "missing file field")
		return
	}
	defer file.Close()

	title := r.FormValue("title")
	if title == "" {
		title = header.Filename
	}

	contentType := header.Header.Get("Content-Type")
	if contentType == "" {
		contentType = "application/octet-stream"
	}

	input := services.CreateDocumentInput{
		Title:       title,
		DocType:     r.FormValue("doc_type"),
		Filename:    header.Filename,
		ContentType: contentType,
		Size:        int(header.Size),
		Data:        file,
	}

	doc, err := h.svc.Create(r.Context(), tenantID, caseID, userID, input)
	if err != nil {
		if err.Error() == "case not found" {
			writeError(w, http.StatusNotFound, "case not found")
			return
		}
		internalError(w, "failed to upload document", err)
		return
	}

	writeJSON(w, http.StatusCreated, doc)
}

func (h *DocumentHandler) Download(w http.ResponseWriter, r *http.Request) {
	tenantID, ok := auth.TenantFromContext(r.Context())
	if !ok {
		writeError(w, http.StatusForbidden, "missing tenant")
		return
	}

	docID, err := uuid.Parse(r.PathValue("docId"))
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid document ID")
		return
	}

	body, contentType, title, err := h.svc.Download(r.Context(), tenantID, docID)
	if err != nil {
		if err.Error() == "document not found" || err.Error() == "document has no file" {
			writeError(w, http.StatusNotFound, "document not found")
			return
		}
		internalError(w, "failed to download document", err)
		return
	}
	defer body.Close()

	w.Header().Set("Content-Type", contentType)
	w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, sanitizeFilename(title)))
	io.Copy(w, body)
}

func (h *DocumentHandler) GetMeta(w http.ResponseWriter, r *http.Request) {
	tenantID, ok := auth.TenantFromContext(r.Context())
	if !ok {
		writeError(w, http.StatusForbidden, "missing tenant")
		return
	}

	docID, err := uuid.Parse(r.PathValue("docId"))
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid document ID")
		return
	}

	doc, err := h.svc.GetByID(r.Context(), tenantID, docID)
	if err != nil {
		internalError(w, "failed to get document metadata", err)
		return
	}
	if doc == nil {
		writeError(w, http.StatusNotFound, "document not found")
		return
	}

	writeJSON(w, http.StatusOK, doc)
}

func (h *DocumentHandler) Delete(w http.ResponseWriter, r *http.Request) {
	tenantID, ok := auth.TenantFromContext(r.Context())
	if !ok {
		writeError(w, http.StatusForbidden, "missing tenant")
		return
	}
	userID, _ := auth.UserFromContext(r.Context())
	role := auth.UserRoleFromContext(r.Context())

	docID, err := uuid.Parse(r.PathValue("docId"))
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid document ID")
		return
	}

	// Check permission: owner/partner can delete any, associate can delete own
	doc, err := h.svc.GetByID(r.Context(), tenantID, docID)
	if err != nil {
		writeError(w, http.StatusInternalServerError, err.Error())
		return
	}
	if doc == nil {
		writeError(w, http.StatusNotFound, "document not found")
		return
	}

	uploaderID := uuid.Nil
	if doc.UploadedBy != nil {
		uploaderID = *doc.UploadedBy
	}
	if !auth.CanDeleteDocument(role, uploaderID, userID) {
		writeError(w, http.StatusForbidden, "insufficient permissions to delete this document")
		return
	}

	if err := h.svc.Delete(r.Context(), tenantID, docID, userID); err != nil {
		writeError(w, http.StatusNotFound, "document not found")
		return
	}

	writeJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
}
