package signoz

import (
	"context"
	"os"
	"reflect"

	"github.com/SigNoz/signoz/pkg/apiserver"
	"github.com/SigNoz/signoz/pkg/apiserver/signozapiserver"
	"github.com/SigNoz/signoz/pkg/authz"
	"github.com/SigNoz/signoz/pkg/flagger"
	"github.com/SigNoz/signoz/pkg/gateway"
	"github.com/SigNoz/signoz/pkg/global"
	"github.com/SigNoz/signoz/pkg/http/handler"
	"github.com/SigNoz/signoz/pkg/instrumentation"
	"github.com/SigNoz/signoz/pkg/modules/authdomain"
	"github.com/SigNoz/signoz/pkg/modules/dashboard"
	"github.com/SigNoz/signoz/pkg/modules/metricsexplorer"
	"github.com/SigNoz/signoz/pkg/modules/organization"
	"github.com/SigNoz/signoz/pkg/modules/preference"
	"github.com/SigNoz/signoz/pkg/modules/promote"
	"github.com/SigNoz/signoz/pkg/modules/role"
	"github.com/SigNoz/signoz/pkg/modules/session"
	"github.com/SigNoz/signoz/pkg/modules/user"
	"github.com/SigNoz/signoz/pkg/types/ctxtypes"
	"github.com/swaggest/jsonschema-go"
	"github.com/swaggest/openapi-go"
	"github.com/swaggest/openapi-go/openapi3"
)

type OpenAPI struct {
	apiserver apiserver.APIServer
	reflector *openapi3.Reflector
	collector *handler.OpenAPICollector
}

func NewOpenAPI(ctx context.Context, instrumentation instrumentation.Instrumentation) (*OpenAPI, error) {
	apiserver, err := signozapiserver.NewFactory(
		struct{ organization.Getter }{},
		struct{ authz.AuthZ }{},
		struct{ organization.Handler }{},
		struct{ user.Handler }{},
		struct{ session.Handler }{},
		struct{ authdomain.Handler }{},
		struct{ preference.Handler }{},
		struct{ global.Handler }{},
		struct{ promote.Handler }{},
		struct{ flagger.Handler }{},
		struct{ dashboard.Module }{},
		struct{ dashboard.Handler }{},
		struct{ metricsexplorer.Handler }{},
		struct{ gateway.Handler }{},
		struct{ role.Getter }{},
		struct{ role.Handler }{},
	).New(ctx, instrumentation.ToProviderSettings(), apiserver.Config{})
	if err != nil {
		return nil, err
	}

	reflector := openapi3.NewReflector()
	reflector.JSONSchemaReflector().DefaultOptions = append(reflector.JSONSchemaReflector().DefaultOptions, jsonschema.InterceptDefName(func(t reflect.Type, defaultDefName string) string {
		if defaultDefName == "RenderSuccessResponse" {
			field, ok := t.FieldByName("Data")
			if !ok {
				return defaultDefName
			}

			return field.Type.Name()
		}

		return defaultDefName
	}))

	reflector.SpecSchema().SetTitle("SigNoz")
	reflector.SpecSchema().SetDescription("OpenTelemetry-Native Logs, Metrics and Traces in a single pane")
	reflector.SpecSchema().SetAPIKeySecurity(ctxtypes.AuthTypeAPIKey.StringValue(), "SigNoz-Api-Key", openapi.InHeader, "API Keys")
	reflector.SpecSchema().SetHTTPBearerTokenSecurity(ctxtypes.AuthTypeTokenizer.StringValue(), "Tokenizer", "Tokens generated by the tokenizer")

	collector := handler.NewOpenAPICollector(reflector)

	return &OpenAPI{
		apiserver: apiserver,
		reflector: reflector,
		collector: collector,
	}, nil
}

func (openapi *OpenAPI) CreateAndWrite(path string) error {
	if err := openapi.apiserver.Router().Walk(openapi.collector.Walker); err != nil {
		return err
	}

	spec, err := openapi.reflector.Spec.MarshalYAML()
	if err != nil {
		return err
	}

	return os.WriteFile(path, spec, 0o600)
}
