package handler

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

	"npm/embed"
	"npm/internal/api/schema"
	"npm/internal/config"
	"npm/internal/logger"

	jsref "github.com/jc21/jsref"
	"github.com/jc21/jsref/provider"
)

var (
	swaggerSchema []byte
	apiDocsSub    fs.FS
)

// Schema simply reads the swagger schema from disk and returns is raw
// Route: GET /schema
func Schema() func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		w.WriteHeader(http.StatusOK)
		fmt.Fprint(w, string(getSchema()))
	}
}

func getSchema() []byte {
	if swaggerSchema == nil {
		apiDocsSub, _ = fs.Sub(embed.APIDocFiles, "api_docs")

		// nolint:gosec
		swaggerSchema, _ = fs.ReadFile(apiDocsSub, "api.swagger.json")

		// Replace {{VERSION}} with Config Version
		swaggerSchema = []byte(strings.ReplaceAll(string(swaggerSchema), "{{VERSION}}", config.Version))

		// Dereference the JSON Schema:
		var schema interface{}
		if err := json.Unmarshal(swaggerSchema, &schema); err != nil {
			logger.Error("SwaggerUnmarshalError", err)
			return nil
		}

		provider := provider.NewIoFS(apiDocsSub, "")
		resolver := jsref.New()
		err := resolver.AddProvider(provider)
		if err != nil {
			logger.Error("SchemaProviderError", err)
		}

		result, err := resolver.Resolve(schema, "", []jsref.Option{jsref.WithRecursiveResolution(true)}...)
		if err != nil {
			logger.Error("SwaggerResolveError", err)
		} else {
			var marshalErr error
			swaggerSchema, marshalErr = json.MarshalIndent(result, "", "  ")
			if marshalErr != nil {
				logger.Error("SwaggerMarshalError", err)
			}
		}
		// End dereference

		// Replace incoming schemas with those we actually use in code
		swaggerSchema = replaceIncomingSchemas(swaggerSchema)
	}
	return swaggerSchema
}

func replaceIncomingSchemas(swaggerSchema []byte) []byte {
	str := string(swaggerSchema)

	// Remember to include the double quotes in the replacement!
	str = strings.ReplaceAll(str, `"{{schema.SetAuth}}"`, schema.SetAuth())
	str = strings.ReplaceAll(str, `"{{schema.GetToken}}"`, schema.GetToken())

	str = strings.ReplaceAll(str, `"{{schema.CreateCertificateAuthority}}"`, schema.CreateCertificateAuthority())
	str = strings.ReplaceAll(str, `"{{schema.UpdateCertificateAuthority}}"`, schema.UpdateCertificateAuthority())

	str = strings.ReplaceAll(str, `"{{schema.CreateCertificate}}"`, schema.CreateCertificate())
	str = strings.ReplaceAll(str, `"{{schema.UpdateCertificate}}"`, schema.UpdateCertificate(""))

	str = strings.ReplaceAll(str, `"{{schema.CreateSetting}}"`, schema.CreateSetting())
	str = strings.ReplaceAll(str, `"{{schema.UpdateSetting}}"`, schema.UpdateSetting())

	str = strings.ReplaceAll(str, `"{{schema.CreateUser}}"`, schema.CreateUser())
	str = strings.ReplaceAll(str, `"{{schema.UpdateUser}}"`, schema.UpdateUser())

	str = strings.ReplaceAll(str, `"{{schema.CreateHost}}"`, schema.CreateHost())
	str = strings.ReplaceAll(str, `"{{schema.UpdateHost}}"`, schema.UpdateHost())

	str = strings.ReplaceAll(str, `"{{schema.CreateHostTemplate}}"`, schema.CreateHostTemplate())
	str = strings.ReplaceAll(str, `"{{schema.UpdateHostTemplate}}"`, schema.UpdateHostTemplate())

	str = strings.ReplaceAll(str, `"{{schema.CreateStream}}"`, schema.CreateStream())
	str = strings.ReplaceAll(str, `"{{schema.UpdateStream}}"`, schema.UpdateStream())

	str = strings.ReplaceAll(str, `"{{schema.CreateDNSProvider}}"`, schema.CreateDNSProvider())
	str = strings.ReplaceAll(str, `"{{schema.UpdateDNSProvider}}"`, schema.UpdateDNSProvider())

	return []byte(str)
}