package api

import (
	"encoding/json"
	"errors"
	"net/http"
)

func registerMetaEndpoints() error {
	if err := RegisterEndpoint(Endpoint{
		Path:        "endpoints",
		Read:        PermitAnyone,
		MimeType:    MimeTypeJSON,
		DataFunc:    listEndpoints,
		Name:        "Export API Endpoints",
		Description: "Returns a list of all registered endpoints and their metadata.",
	}); err != nil {
		return err
	}

	if err := RegisterEndpoint(Endpoint{
		Path:        "auth/permissions",
		Read:        Dynamic,
		StructFunc:  permissions,
		Name:        "View Current Permissions",
		Description: "Returns the current permissions assigned to the request.",
	}); err != nil {
		return err
	}

	if err := RegisterEndpoint(Endpoint{
		Path:        "auth/bearer",
		Read:        Dynamic,
		HandlerFunc: authBearer,
		Name:        "Request HTTP Bearer Auth",
		Description: "Returns an HTTP Bearer Auth request, if not authenticated.",
	}); err != nil {
		return err
	}

	if err := RegisterEndpoint(Endpoint{
		Path:        "auth/basic",
		Read:        Dynamic,
		HandlerFunc: authBasic,
		Name:        "Request HTTP Basic Auth",
		Description: "Returns an HTTP Basic Auth request, if not authenticated.",
	}); err != nil {
		return err
	}

	if err := RegisterEndpoint(Endpoint{
		Path:        "auth/reset",
		Read:        PermitAnyone,
		HandlerFunc: authReset,
		Name:        "Reset Authenticated Session",
		Description: "Resets authentication status internally and in the browser.",
	}); err != nil {
		return err
	}

	return nil
}

func listEndpoints(ar *Request) (data []byte, err error) {
	data, err = json.Marshal(ExportEndpoints())
	return
}

func permissions(ar *Request) (i interface{}, err error) {
	if ar.AuthToken == nil {
		return nil, errors.New("authentication token missing")
	}

	return struct {
		Read      Permission
		Write     Permission
		ReadRole  string
		WriteRole string
	}{
		Read:      ar.AuthToken.Read,
		Write:     ar.AuthToken.Write,
		ReadRole:  ar.AuthToken.Read.Role(),
		WriteRole: ar.AuthToken.Write.Role(),
	}, nil
}

func authBearer(w http.ResponseWriter, r *http.Request) {
	// Check if authenticated by checking read permission.
	ar := GetAPIRequest(r)
	if ar.AuthToken.Read != PermitAnyone {
		TextResponse(w, r, "Authenticated.")
		return
	}

	// Respond with desired authentication header.
	w.Header().Set(
		"WWW-Authenticate",
		`Bearer realm="Portmaster API" domain="/"`,
	)
	http.Error(w, "Authorization required.", http.StatusUnauthorized)
}

func authBasic(w http.ResponseWriter, r *http.Request) {
	// Check if authenticated by checking read permission.
	ar := GetAPIRequest(r)
	if ar.AuthToken.Read != PermitAnyone {
		TextResponse(w, r, "Authenticated.")
		return
	}

	// Respond with desired authentication header.
	w.Header().Set(
		"WWW-Authenticate",
		`Basic realm="Portmaster API" domain="/"`,
	)
	http.Error(w, "Authorization required.", http.StatusUnauthorized)
}

func authReset(w http.ResponseWriter, r *http.Request) {
	// Get session cookie from request and delete session if exists.
	c, err := r.Cookie(sessionCookieName)
	if err == nil {
		deleteSession(c.Value)
	}

	// Delete session and cookie.
	http.SetCookie(w, &http.Cookie{
		Name:   sessionCookieName,
		MaxAge: -1, // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
	})

	// Request client to also reset all data.
	w.Header().Set("Clear-Site-Data", "*")

	// Set HTTP Auth Realm without requesting authorization.
	w.Header().Set("WWW-Authenticate", `None realm="Portmaster API"`)

	// Reply with 401 Unauthorized in order to clear HTTP Basic Auth data.
	http.Error(w, "Session deleted.", http.StatusUnauthorized)
}