package server

import (
	"encoding/json"
	"net/http"
	"strings"

	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/sandbox0-ai/sandbox0/pkg/ctldapi"
	"github.com/sandbox0-ai/sandbox0/pkg/sandboxprobe"
)

type Controller interface {
	Pause(r *http.Request, sandboxID string) (ctldapi.PauseResponse, int)
	Resume(r *http.Request, sandboxID string) (ctldapi.ResumeResponse, int)
	Probe(r *http.Request, sandboxID string, kind sandboxprobe.Kind) (sandboxprobe.Response, int)
	ProbePod(r *http.Request, namespace, name string, kind sandboxprobe.Kind) (sandboxprobe.Response, int)
}

type VolumePortalController interface {
	BindVolumePortal(r *http.Request, req ctldapi.BindVolumePortalRequest) (ctldapi.BindVolumePortalResponse, int)
	UnbindVolumePortal(r *http.Request, req ctldapi.UnbindVolumePortalRequest) (ctldapi.UnbindVolumePortalResponse, int)
	CheckVolumePortals(r *http.Request, req ctldapi.CheckVolumePortalsRequest) (ctldapi.CheckVolumePortalsResponse, int)
	AttachVolumeOwner(r *http.Request, req ctldapi.AttachVolumeOwnerRequest) (ctldapi.AttachVolumeOwnerResponse, int)
	ReleaseVolumeOwner(r *http.Request, req ctldapi.ReleaseVolumeOwnerRequest) (ctldapi.ReleaseVolumeOwnerResponse, int)
	PrepareVolumeSnapshotCheckpoint(r *http.Request, req ctldapi.PrepareVolumeSnapshotCheckpointRequest) (ctldapi.PrepareVolumeSnapshotCheckpointResponse, int)
	CompleteVolumeSnapshotCheckpoint(r *http.Request, req ctldapi.CompleteVolumeSnapshotCheckpointRequest) (ctldapi.CompleteVolumeSnapshotCheckpointResponse, int)
	AbortVolumeSnapshotCheckpoint(r *http.Request, req ctldapi.AbortVolumeSnapshotCheckpointRequest) (ctldapi.AbortVolumeSnapshotCheckpointResponse, int)
}

type RootFSController interface {
	InspectRootFS(r *http.Request, req ctldapi.InspectRootFSRequest) (ctldapi.InspectRootFSResponse, int)
	SaveRootFS(r *http.Request, req ctldapi.SaveRootFSRequest) (ctldapi.SaveRootFSResponse, int)
	ApplyRootFS(r *http.Request, req ctldapi.ApplyRootFSRequest) (ctldapi.ApplyRootFSResponse, int)
}

type RootFSSnapshotController interface {
	PrepareRootFSSnapshot(r *http.Request, req ctldapi.PrepareRootFSSnapshotRequest) (ctldapi.PrepareRootFSSnapshotResponse, int)
	PublishRootFSSnapshot(r *http.Request, req ctldapi.PublishRootFSSnapshotRequest) (ctldapi.PublishRootFSSnapshotResponse, int)
	AbortRootFSSnapshot(r *http.Request, req ctldapi.AbortRootFSSnapshotRequest) (ctldapi.AbortRootFSSnapshotResponse, int)
}

type MountedVolumeController interface {
	MountedVolumeHandler() http.Handler
}

type NotImplementedController struct{}

func (NotImplementedController) Pause(_ *http.Request, _ string) (ctldapi.PauseResponse, int) {
	return ctldapi.PauseResponse{Paused: false, Error: "ctld pause not implemented"}, http.StatusNotImplemented
}

func (NotImplementedController) Resume(_ *http.Request, _ string) (ctldapi.ResumeResponse, int) {
	return ctldapi.ResumeResponse{Resumed: false, Error: "ctld resume not implemented"}, http.StatusNotImplemented
}

func (NotImplementedController) Probe(_ *http.Request, _ string, kind sandboxprobe.Kind) (sandboxprobe.Response, int) {
	return sandboxprobe.Failed(kind, "ProbeNotImplemented", "ctld sandbox probe not implemented", nil), http.StatusNotImplemented
}

func (NotImplementedController) ProbePod(_ *http.Request, _, _ string, kind sandboxprobe.Kind) (sandboxprobe.Response, int) {
	return sandboxprobe.Failed(kind, "ProbeNotImplemented", "ctld pod probe not implemented", nil), http.StatusNotImplemented
}

func (NotImplementedController) InspectRootFS(_ *http.Request, _ ctldapi.InspectRootFSRequest) (ctldapi.InspectRootFSResponse, int) {
	return ctldapi.InspectRootFSResponse{Error: "ctld rootfs inspect not implemented"}, http.StatusNotImplemented
}

func (NotImplementedController) SaveRootFS(_ *http.Request, _ ctldapi.SaveRootFSRequest) (ctldapi.SaveRootFSResponse, int) {
	return ctldapi.SaveRootFSResponse{Error: "ctld rootfs save not implemented"}, http.StatusNotImplemented
}

func (NotImplementedController) PrepareRootFSSnapshot(_ *http.Request, _ ctldapi.PrepareRootFSSnapshotRequest) (ctldapi.PrepareRootFSSnapshotResponse, int) {
	return ctldapi.PrepareRootFSSnapshotResponse{Error: "ctld rootfs snapshot prepare not implemented"}, http.StatusNotImplemented
}

func (NotImplementedController) PublishRootFSSnapshot(_ *http.Request, _ ctldapi.PublishRootFSSnapshotRequest) (ctldapi.PublishRootFSSnapshotResponse, int) {
	return ctldapi.PublishRootFSSnapshotResponse{Error: "ctld rootfs snapshot publish not implemented"}, http.StatusNotImplemented
}

func (NotImplementedController) AbortRootFSSnapshot(_ *http.Request, _ ctldapi.AbortRootFSSnapshotRequest) (ctldapi.AbortRootFSSnapshotResponse, int) {
	return ctldapi.AbortRootFSSnapshotResponse{Error: "ctld rootfs snapshot abort not implemented"}, http.StatusNotImplemented
}

func (NotImplementedController) ApplyRootFS(_ *http.Request, _ ctldapi.ApplyRootFSRequest) (ctldapi.ApplyRootFSResponse, int) {
	return ctldapi.ApplyRootFSResponse{Error: "ctld rootfs apply not implemented"}, http.StatusNotImplemented
}

func NewMux(controller Controller) http.Handler {
	if controller == nil {
		controller = NotImplementedController{}
	}
	mux := http.NewServeMux()
	mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte("ok"))
	})
	mux.HandleFunc("/readyz", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte("ok"))
	})
	mux.Handle("/metrics", promhttp.Handler())
	if mountedController, ok := controller.(MountedVolumeController); ok {
		if mountedHandler := mountedController.MountedVolumeHandler(); mountedHandler != nil {
			mux.Handle("/sandboxvolumes/", mountedHandler)
		}
	}
	mux.HandleFunc("/api/v1/volume-portals/bind", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		volumeController, ok := controller.(VolumePortalController)
		if !ok {
			w.WriteHeader(http.StatusNotImplemented)
			_ = json.NewEncoder(w).Encode(ctldapi.BindVolumePortalResponse{})
			return
		}
		var req ctldapi.BindVolumePortalRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			_ = json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
			return
		}
		w.Header().Set("Content-Type", "application/json")
		resp, status := volumeController.BindVolumePortal(r, req)
		w.WriteHeader(status)
		_ = json.NewEncoder(w).Encode(resp)
	})
	mux.HandleFunc("/api/v1/volume-portals/unbind", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		volumeController, ok := controller.(VolumePortalController)
		if !ok {
			w.WriteHeader(http.StatusNotImplemented)
			_ = json.NewEncoder(w).Encode(ctldapi.UnbindVolumePortalResponse{Error: "ctld volume portals not implemented"})
			return
		}
		var req ctldapi.UnbindVolumePortalRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			_ = json.NewEncoder(w).Encode(ctldapi.UnbindVolumePortalResponse{Error: err.Error()})
			return
		}
		w.Header().Set("Content-Type", "application/json")
		resp, status := volumeController.UnbindVolumePortal(r, req)
		w.WriteHeader(status)
		_ = json.NewEncoder(w).Encode(resp)
	})
	mux.HandleFunc("/api/v1/volume-portals/check", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		volumeController, ok := controller.(VolumePortalController)
		if !ok {
			w.WriteHeader(http.StatusNotImplemented)
			_ = json.NewEncoder(w).Encode(ctldapi.CheckVolumePortalsResponse{Error: "ctld volume portals not implemented"})
			return
		}
		var req ctldapi.CheckVolumePortalsRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			_ = json.NewEncoder(w).Encode(ctldapi.CheckVolumePortalsResponse{Error: err.Error()})
			return
		}
		w.Header().Set("Content-Type", "application/json")
		resp, status := volumeController.CheckVolumePortals(r, req)
		w.WriteHeader(status)
		_ = json.NewEncoder(w).Encode(resp)
	})
	mux.HandleFunc("/api/v1/volume-portals/owners/attach", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		volumeController, ok := controller.(VolumePortalController)
		if !ok {
			w.WriteHeader(http.StatusNotImplemented)
			_ = json.NewEncoder(w).Encode(ctldapi.AttachVolumeOwnerResponse{Error: "ctld volume owners not implemented"})
			return
		}
		var req ctldapi.AttachVolumeOwnerRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			_ = json.NewEncoder(w).Encode(ctldapi.AttachVolumeOwnerResponse{Error: err.Error()})
			return
		}
		w.Header().Set("Content-Type", "application/json")
		resp, status := volumeController.AttachVolumeOwner(r, req)
		w.WriteHeader(status)
		_ = json.NewEncoder(w).Encode(resp)
	})
	mux.HandleFunc("/api/v1/volume-portals/owners/release", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		volumeController, ok := controller.(VolumePortalController)
		if !ok {
			w.WriteHeader(http.StatusNotImplemented)
			_ = json.NewEncoder(w).Encode(ctldapi.ReleaseVolumeOwnerResponse{Error: "ctld volume owners not implemented"})
			return
		}
		var req ctldapi.ReleaseVolumeOwnerRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			_ = json.NewEncoder(w).Encode(ctldapi.ReleaseVolumeOwnerResponse{Error: err.Error()})
			return
		}
		w.Header().Set("Content-Type", "application/json")
		resp, status := volumeController.ReleaseVolumeOwner(r, req)
		w.WriteHeader(status)
		_ = json.NewEncoder(w).Encode(resp)
	})
	mux.HandleFunc("/api/v1/volume-portals/snapshot-checkpoints/prepare", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		volumeController, ok := controller.(VolumePortalController)
		if !ok {
			w.WriteHeader(http.StatusNotImplemented)
			_ = json.NewEncoder(w).Encode(ctldapi.PrepareVolumeSnapshotCheckpointResponse{Error: "ctld volume snapshot checkpoint not implemented"})
			return
		}
		var req ctldapi.PrepareVolumeSnapshotCheckpointRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			_ = json.NewEncoder(w).Encode(ctldapi.PrepareVolumeSnapshotCheckpointResponse{Error: err.Error()})
			return
		}
		w.Header().Set("Content-Type", "application/json")
		resp, status := volumeController.PrepareVolumeSnapshotCheckpoint(r, req)
		w.WriteHeader(status)
		_ = json.NewEncoder(w).Encode(resp)
	})
	mux.HandleFunc("/api/v1/volume-portals/snapshot-checkpoints/complete", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		volumeController, ok := controller.(VolumePortalController)
		if !ok {
			w.WriteHeader(http.StatusNotImplemented)
			_ = json.NewEncoder(w).Encode(ctldapi.CompleteVolumeSnapshotCheckpointResponse{Error: "ctld volume snapshot checkpoint not implemented"})
			return
		}
		var req ctldapi.CompleteVolumeSnapshotCheckpointRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			_ = json.NewEncoder(w).Encode(ctldapi.CompleteVolumeSnapshotCheckpointResponse{Error: err.Error()})
			return
		}
		w.Header().Set("Content-Type", "application/json")
		resp, status := volumeController.CompleteVolumeSnapshotCheckpoint(r, req)
		w.WriteHeader(status)
		_ = json.NewEncoder(w).Encode(resp)
	})
	mux.HandleFunc("/api/v1/volume-portals/snapshot-checkpoints/abort", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		volumeController, ok := controller.(VolumePortalController)
		if !ok {
			w.WriteHeader(http.StatusNotImplemented)
			_ = json.NewEncoder(w).Encode(ctldapi.AbortVolumeSnapshotCheckpointResponse{Error: "ctld volume snapshot checkpoint not implemented"})
			return
		}
		var req ctldapi.AbortVolumeSnapshotCheckpointRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			_ = json.NewEncoder(w).Encode(ctldapi.AbortVolumeSnapshotCheckpointResponse{Error: err.Error()})
			return
		}
		w.Header().Set("Content-Type", "application/json")
		resp, status := volumeController.AbortVolumeSnapshotCheckpoint(r, req)
		w.WriteHeader(status)
		_ = json.NewEncoder(w).Encode(resp)
	})
	mux.HandleFunc("/api/v1/rootfs/inspect", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		rootFSController, ok := controller.(RootFSController)
		if !ok {
			w.WriteHeader(http.StatusNotImplemented)
			_ = json.NewEncoder(w).Encode(ctldapi.InspectRootFSResponse{Error: "ctld rootfs inspect not implemented"})
			return
		}
		var req ctldapi.InspectRootFSRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			_ = json.NewEncoder(w).Encode(ctldapi.InspectRootFSResponse{Error: err.Error()})
			return
		}
		w.Header().Set("Content-Type", "application/json")
		resp, status := rootFSController.InspectRootFS(r, req)
		w.WriteHeader(status)
		_ = json.NewEncoder(w).Encode(resp)
	})
	mux.HandleFunc("/api/v1/rootfs/save", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		rootFSController, ok := controller.(RootFSController)
		if !ok {
			w.WriteHeader(http.StatusNotImplemented)
			_ = json.NewEncoder(w).Encode(ctldapi.SaveRootFSResponse{Error: "ctld rootfs save not implemented"})
			return
		}
		var req ctldapi.SaveRootFSRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			_ = json.NewEncoder(w).Encode(ctldapi.SaveRootFSResponse{Error: err.Error()})
			return
		}
		w.Header().Set("Content-Type", "application/json")
		resp, status := rootFSController.SaveRootFS(r, req)
		w.WriteHeader(status)
		_ = json.NewEncoder(w).Encode(resp)
	})
	mux.HandleFunc("/api/v1/rootfs/snapshots/prepare", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		rootFSController, ok := controller.(RootFSSnapshotController)
		if !ok {
			w.WriteHeader(http.StatusNotImplemented)
			_ = json.NewEncoder(w).Encode(ctldapi.PrepareRootFSSnapshotResponse{Error: "ctld rootfs snapshot prepare not implemented"})
			return
		}
		var req ctldapi.PrepareRootFSSnapshotRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			_ = json.NewEncoder(w).Encode(ctldapi.PrepareRootFSSnapshotResponse{Error: err.Error()})
			return
		}
		w.Header().Set("Content-Type", "application/json")
		resp, status := rootFSController.PrepareRootFSSnapshot(r, req)
		w.WriteHeader(status)
		_ = json.NewEncoder(w).Encode(resp)
	})
	mux.HandleFunc("/api/v1/rootfs/snapshots/publish", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		rootFSController, ok := controller.(RootFSSnapshotController)
		if !ok {
			w.WriteHeader(http.StatusNotImplemented)
			_ = json.NewEncoder(w).Encode(ctldapi.PublishRootFSSnapshotResponse{Error: "ctld rootfs snapshot publish not implemented"})
			return
		}
		var req ctldapi.PublishRootFSSnapshotRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			_ = json.NewEncoder(w).Encode(ctldapi.PublishRootFSSnapshotResponse{Error: err.Error()})
			return
		}
		w.Header().Set("Content-Type", "application/json")
		resp, status := rootFSController.PublishRootFSSnapshot(r, req)
		w.WriteHeader(status)
		_ = json.NewEncoder(w).Encode(resp)
	})
	mux.HandleFunc("/api/v1/rootfs/snapshots/abort", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		rootFSController, ok := controller.(RootFSSnapshotController)
		if !ok {
			w.WriteHeader(http.StatusNotImplemented)
			_ = json.NewEncoder(w).Encode(ctldapi.AbortRootFSSnapshotResponse{Error: "ctld rootfs snapshot abort not implemented"})
			return
		}
		var req ctldapi.AbortRootFSSnapshotRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			_ = json.NewEncoder(w).Encode(ctldapi.AbortRootFSSnapshotResponse{Error: err.Error()})
			return
		}
		w.Header().Set("Content-Type", "application/json")
		resp, status := rootFSController.AbortRootFSSnapshot(r, req)
		w.WriteHeader(status)
		_ = json.NewEncoder(w).Encode(resp)
	})
	mux.HandleFunc("/api/v1/rootfs/apply", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		rootFSController, ok := controller.(RootFSController)
		if !ok {
			w.WriteHeader(http.StatusNotImplemented)
			_ = json.NewEncoder(w).Encode(ctldapi.ApplyRootFSResponse{Error: "ctld rootfs apply not implemented"})
			return
		}
		var req ctldapi.ApplyRootFSRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			_ = json.NewEncoder(w).Encode(ctldapi.ApplyRootFSResponse{Error: err.Error()})
			return
		}
		w.Header().Set("Content-Type", "application/json")
		resp, status := rootFSController.ApplyRootFS(r, req)
		w.WriteHeader(status)
		_ = json.NewEncoder(w).Encode(resp)
	})
	mux.HandleFunc("/api/v1/sandboxes/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}

		path := strings.TrimPrefix(r.URL.Path, "/api/v1/sandboxes/")
		parts := strings.Split(strings.Trim(path, "/"), "/")
		if len(parts) < 2 || parts[0] == "" {
			w.WriteHeader(http.StatusNotFound)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		sandboxID := parts[0]
		switch {
		case len(parts) == 2 && parts[1] == "pause":
			resp, status := controller.Pause(r, sandboxID)
			w.WriteHeader(status)
			_ = json.NewEncoder(w).Encode(resp)
		case len(parts) == 2 && parts[1] == "resume":
			resp, status := controller.Resume(r, sandboxID)
			w.WriteHeader(status)
			_ = json.NewEncoder(w).Encode(resp)
		case len(parts) == 3 && parts[1] == "probes":
			resp, status := controller.Probe(r, sandboxID, sandboxprobe.Kind(parts[2]))
			w.WriteHeader(status)
			_ = json.NewEncoder(w).Encode(resp)
		default:
			w.WriteHeader(http.StatusNotFound)
		}
	})
	mux.HandleFunc("/api/v1/pods/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}

		path := strings.TrimPrefix(r.URL.Path, "/api/v1/pods/")
		parts := strings.Split(strings.Trim(path, "/"), "/")
		if len(parts) != 4 || parts[0] == "" || parts[1] == "" || parts[2] != "probes" {
			w.WriteHeader(http.StatusNotFound)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		resp, status := controller.ProbePod(r, parts[0], parts[1], sandboxprobe.Kind(parts[3]))
		w.WriteHeader(status)
		_ = json.NewEncoder(w).Encode(resp)
	})
	return mux
}
