// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//     http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package resource

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

	rcHealthz "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/healthz"
	"github.com/go-logr/logr"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/healthz"
)

const (
	GetNodeResourcesPath    = "/node/"
	GetAllResourcesPath     = "/resources/all"
	GetResourcesSummaryPath = "/resources/summary"
)

type IntrospectHandler struct {
	Log             logr.Logger
	BindAddress     string
	ResourceManager ResourceManager
}

// StartENICleaner starts the ENI Cleaner routine that cleans up dangling ENIs created by the controller
func (i *IntrospectHandler) Start(_ context.Context) error {
	i.Log.Info("starting introspection API")

	mux := http.NewServeMux()
	mux.HandleFunc(GetAllResourcesPath, i.ResourceHandler)
	mux.HandleFunc(GetNodeResourcesPath, i.NodeResourceHandler)
	mux.HandleFunc(GetResourcesSummaryPath, i.ResourceSummaryHandler)

	// Should this be a fatal error?
	err := http.ListenAndServe(i.BindAddress, mux) // #nosec G114
	if err != nil {
		i.Log.Error(err, "failed to run introspect API")
	}
	return err
}

// ResourceHandler returns all the nodes associated with the resource
func (i *IntrospectHandler) ResourceHandler(w http.ResponseWriter, _ *http.Request) {
	response := make(map[string]interface{})
	for resourceName, provider := range i.ResourceManager.GetResourceProviders() {
		data := provider.Introspect()
		response[resourceName] = data
	}

	jsonData, err := json.MarshalIndent(response, "", "\t")
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
		return
	}

	w.Header().Set("content-type", "application/json")
	w.WriteHeader(http.StatusOK)
	w.Write(jsonData)
}

// NodeResourceHandler returns all the resources associated with the Node
func (i *IntrospectHandler) NodeResourceHandler(w http.ResponseWriter, r *http.Request) {
	nodeName := r.URL.Path[len(GetNodeResourcesPath):]

	response := make(map[string]interface{})
	for resourceName, provider := range i.ResourceManager.GetResourceProviders() {
		data := provider.IntrospectNode(nodeName)
		if data != nil {
			response[resourceName] = data
		}
	}

	jsonData, err := json.MarshalIndent(response, "", "\t")
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
		return
	}

	w.Header().Set("content-type", "application/json")
	w.WriteHeader(http.StatusOK)
	w.Write(jsonData)
}

// ResourceSummaryHandler returns all the resources associated with the Node
func (i *IntrospectHandler) ResourceSummaryHandler(w http.ResponseWriter, r *http.Request) {
	response := make(map[string]interface{})
	for resourceName, provider := range i.ResourceManager.GetResourceProviders() {
		data := provider.IntrospectSummary()
		response[resourceName] = data
	}

	jsonData, err := json.MarshalIndent(response, "", "\t")
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
		return
	}

	w.Header().Set("content-type", "application/json")
	w.WriteHeader(http.StatusOK)
	w.Write(jsonData)
}

func (i *IntrospectHandler) SetupWithManager(mgr ctrl.Manager, healthzHanlder *rcHealthz.HealthzHandler) error {
	// add health check on subpath for introspect controller
	healthzHanlder.AddControllersHealthCheckers(
		map[string]healthz.Checker{"health-introspect-controller": rcHealthz.SimplePing("Introspect controller", i.Log)},
	)

	return mgr.Add(i)
}
