package dbaasui

import (
	"bytes"
	"context"
	"fmt"
	"net/http"
)

const uiNamespace = "cp9-global"

// ReadinessInspection owns the one live UI readiness state machine shared by
// lifecycle setup and cp9 status. Callers supply only their deployment and
// identity policy: lifecycle may wait/mutate, while status remains read-only.
type ReadinessInspection struct {
	URL                   string
	ExpectedRuntimeConfig []byte
	SeedIdentity          string
	Run                   CommandRunner
	InspectDeployment     func(context.Context) (string, error)
	InspectIdentity       func(context.Context, string, CommandRunner) Dimension
	Auth0HTTPClient       *http.Client
	Auth0ReadToken        string
}

// InspectReadiness returns complete evidence. Only session-owned static
// failures are returned as errors; shared Auth0/identity drift remains a
// degraded UI dimension.
func InspectReadiness(ctx context.Context, inspection *ReadinessInspection) (*ReadinessEvidence, error) {
	evidence := &ReadinessEvidence{URL: inspection.URL}
	failStatic := func(message string, err error) (*ReadinessEvidence, error) {
		evidence.StaticMessage = message
		evidence.Recompute()
		return evidence, err
	}
	if inspection.Run == nil || inspection.InspectDeployment == nil {
		return failStatic("dbaas-ui live inspector is unavailable", fmt.Errorf("dbaas-ui live inspector is unavailable"))
	}

	image, err := inspection.InspectDeployment(ctx)
	evidence.Image = image
	if err != nil {
		return failStatic("dbaas-ui Deployment is unavailable", err)
	}
	if err := ValidateImageReference(image); err != nil {
		return failStatic("dbaas-ui is not using an approved immutable image", err)
	}
	runtimeConfig, err := inspection.Run(
		ctx, nil,
		"-n", uiNamespace,
		"get", "configmap", "dbaas-ui-runtime",
		"-o", "jsonpath={.data.config\\.js}",
	)
	if err != nil {
		return failStatic(
			"dbaas-ui runtime config could not be inspected",
			fmt.Errorf("read dbaas-ui runtime config: %w\n%s", err, string(runtimeConfig)),
		)
	}
	if !bytes.Equal(runtimeConfig, inspection.ExpectedRuntimeConfig) {
		return failStatic("dbaas-ui runtime config drifted from the resolved Session", fmt.Errorf("dbaas-ui runtime config drift"))
	}
	evidence.UIStaticReady = true
	evidence.StaticMessage = "dbaas-ui Deployment, immutable account-414 image, and runtime config are ready"

	accountConfig, accountErr := inspection.Run(
		ctx, nil,
		"-n", uiNamespace,
		"get", "configmap", "account-central-conf",
		"-o", "jsonpath={.data.config\\.yaml}",
	)
	if accountErr != nil {
		evidence.AuthMessage = "session Account Auth0 config is unavailable"
	} else if account, parseErr := ParseAccountAuth0Config(string(accountConfig)); parseErr != nil {
		evidence.AuthMessage = parseErr.Error()
	} else if client, fetchErr := FetchAuth0Client(ctx, inspection.Auth0HTTPClient, inspection.Auth0ReadToken); fetchErr != nil {
		evidence.AuthMessage = fetchErr.Error()
	} else if validateErr := ValidateAuth0Contract(inspection.URL, account, client); validateErr != nil {
		evidence.AuthMessage = validateErr.Error()
	} else if lookupErr := VerifyAuth0UserLookup(
		ctx,
		inspection.Auth0HTTPClient,
		inspection.Auth0ReadToken,
		inspection.SeedIdentity,
	); lookupErr != nil {
		evidence.AuthMessage = lookupErr.Error()
	} else {
		evidence.UIAuthReady = true
		evidence.AuthMessage = "shared tidb-dev Auth0 client and seed-user lookup match the CP9 contract"
	}

	if inspection.InspectIdentity == nil {
		evidence.IdentityMessage = "Account identity inspector is unavailable"
	} else {
		identity := inspection.InspectIdentity(ctx, inspection.SeedIdentity, inspection.Run)
		evidence.UIIdentityReady = identity.Ready
		evidence.IdentityMessage = identity.Message
	}
	evidence.Recompute()
	return evidence, nil
}
