package controlplane

import (
	"database/sql"
	"encoding/json"
	"strings"

	"github.com/pingcap-inc/nexuscrm-controlplane/packspec"
	"github.com/pingcap-inc/nexuscrm-controlplane/specdrift"
)

// VerifyStandardFieldSpecDrift walks standard_objects_spec.json and logs a
// WARNING for each field where the spec disagrees with _System_Field on
// Type, Required, DefaultValue, or (for picklists) the Options value set.
//
// Missing-on-live rows are reported as a count (admin soft-deletes are
// not auto-recreated by pack apply, and a per-field warning would flood
// fresh installs where pack apply has not yet created the rows) — i.e.
// strictMissing=false.
//
// Tenant-owned rows (declared through the tenant field manifest) are skipped
// here; the tenant pack carries its own Verify that runs only on deployments
// that actually applied the overlay.
func VerifyStandardFieldSpecDrift(rawDB *sql.DB) error {
	return VerifyStandardFieldSpecDriftByCategory(rawDB, "system")
}

func VerifyStandardFieldSpecDriftByCategory(rawDB *sql.DB, category string) error {
	specJSON, err := packspec.StandardObjectsSpecByCategoryJSON(category)
	if err != nil {
		return err
	}
	return specdrift.Verify(
		rawDB,
		specJSON,
		"standard_objects_spec.json:"+category,
		tenantOwnedFieldSet(),
		false,
	)
}

// tenantOwnedFieldSet returns "object.field" keys for fields declared in the
// tenant field manifest. Used by VerifyStandardFieldSpecDrift to skip fields
// whose live values are expected to differ from the standard spec when the
// tenant overlay has been applied (tenant is authoritative there).
// On greenfield deployments the tenant overlay is never applied, so
// these rows simply don't exist and the filter is a no-op.
func tenantOwnedFieldSet() map[string]bool {
	type tenantField struct {
		APIName string `json:"apiName"`
	}
	type tenantObj struct {
		APIName string        `json:"apiName"`
		Fields  []tenantField `json:"fields"`
	}
	manifest := packspec.TenantFieldManifestJSON()
	if len(manifest) == 0 {
		return nil
	}
	var specs []tenantObj
	if err := json.Unmarshal(manifest, &specs); err != nil {
		return nil
	}
	set := make(map[string]bool)
	for _, spec := range specs {
		for _, f := range spec.Fields {
			set[strings.ToLower(spec.APIName)+"."+strings.ToLower(f.APIName)] = true
		}
	}
	return set
}
