// Package handler provides the HTTP handler for the marka server
package handler

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"

	"git.max-richter.dev/max/marka/renderer"
	"git.max-richter.dev/max/marka/server/internal/adapters"
)

type Handler struct {
	adapter adapters.FileAdapter
	apiKey  string
}

func (h *Handler) get(w http.ResponseWriter, target string) {
	entry, err := h.adapter.Read(target)
	if err != nil {
		if errors.Is(err, adapters.ErrNotFound) {
			writeError(w, http.StatusNotFound, err)
			return
		}
		writeError(w, http.StatusInternalServerError, err)
		return
	}

	if entry.Type == "file" {
		// For non-markdown files, content is []byte, write it directly
		if contentBytes, ok := entry.Content.([]byte); ok {
			w.Header().Set("Content-Type", entry.MIME)
			w.Write(contentBytes)
			return
		}
		// For markdown files, content is parsed, return the whole Entry as JSON
		writeJSON(w, http.StatusOK, entry)
		return
	}

	// For directories, return the whole Entry as JSON
	if entry.Type == "dir" {
		writeJSON(w, http.StatusOK, entry)
		return
	}

	writeError(w, http.StatusInternalServerError, errors.New("unknown entry type"))
}

func (h *Handler) post(w http.ResponseWriter, r *http.Request, target string) {
	if h.apiKey != "" {
		if r.Header.Get("Authentication") != h.apiKey {
			writeError(w, http.StatusUnauthorized, errors.New("invalid api key"))
			return
		}
	} else {
		writeError(w, http.StatusUnauthorized, errors.New("invalid api key"))
		return
	}

	body, err := io.ReadAll(r.Body)
	if err != nil {
		writeError(w, http.StatusBadRequest, fmt.Errorf("failed to decode body: %w", err))
		return
	}
	defer r.Body.Close()

	contentType := r.Header.Get("Content-Type")
	isResource := strings.HasPrefix(contentType, "application/json") && strings.HasSuffix(target, ".md")

	var contentToWrite []byte
	if isResource {
		renderedContent, err := renderer.RenderFile(body)
		if err != nil {
			writeError(w, http.StatusInternalServerError, fmt.Errorf("failed to render file: %w", err))
			return
		}
		contentToWrite = renderedContent
	} else {
		contentToWrite = body
	}

	if err := h.adapter.Write(target, contentToWrite); err != nil {
		writeError(w, http.StatusInternalServerError, fmt.Errorf("failed to write file: %w", err))
		return
	}

	if isResource {
		var data map[string]any
		if err := json.Unmarshal(body, &data); err != nil && strings.HasSuffix(target, ".md") {
			writeError(w, http.StatusInternalServerError, fmt.Errorf("failed to decode body: %w", err))
			return
		}
		writeJSON(w, http.StatusOK, data)
		return
	}

	writeJSON(w, http.StatusOK, map[string]any{"success": true})
}

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	reqPath := r.URL.Path
	if reqPath == "" {
		reqPath = "/"
	}

	target := cleanURLLike(reqPath)

	fmt.Printf("[serve] %s %s\n", r.Method, target)

	switch r.Method {
	case http.MethodGet:
		h.get(w, target)
	case http.MethodPost:
		h.post(w, r, target)
	default:
		writeError(w, http.StatusMethodNotAllowed, errors.New("method not allowed"))
	}
}

func NewHandler(adapter adapters.FileAdapter, apiKey string) http.Handler {
	return &Handler{
		adapter: adapter,
		apiKey:  apiKey,
	}
}
