package controlplane

import (
	"context"
	"fmt"

	"github.com/nexuscrm/backend/internal/bootstrap"
	"github.com/nexuscrm/backend/internal/controlplane/packs"
	"github.com/nexuscrm/backend/internal/infrastructure/database"
)

// Verify performs read-only readiness verification. It checks:
//  1. Schema registry health (hardened — returns real errors)
//  2. All assertions including managed metadata drift detection
//  3. Per-pack verification for runtime-required packs only (DevSample excluded)
//
// This is the POD-READINESS contract: pass `DeepChecks: false` so packs
// run only the metadata-only gates that are safe on every restart and
// tolerant of concurrent writers. For the operator-initiated
// `cmd/setup verify` path use VerifyDeep instead.
//
// If any check fails, the error describes what is wrong and the operator
// should run "cmd/setup apply" to fix it.
func Verify(ctx context.Context, db *database.TiDBConnection) error {
	return verifyImpl(ctx, db, false)
}

// VerifyDeep is the operator-initiated verify (`cmd/setup verify`). It
// runs the full gate set, including live-data scans that assume
// writers are quiesced. Do NOT call from the pod-readiness path —
// live-data scans scale with table size and can oscillate under
// concurrent writers.
func VerifyDeep(ctx context.Context, db *database.TiDBConnection) error {
	return verifyImpl(ctx, db, true)
}

// VerifyProfile is a narrow operator-facing profile convergence signal.
// It intentionally skips unrelated pack/assertion noise and checks only the
// schema prerequisites plus profile permission coverage drift.
func VerifyProfile(ctx context.Context, db *database.TiDBConnection) error {
	if err := bootstrap.VerifySchemaReadiness(db); err != nil {
		return fmt.Errorf("schema readiness: %w", err)
	}
	if err := VerifyProfilePermissionCoverage(db.DB()); err != nil {
		return fmt.Errorf("profile permission coverage check: %w", err)
	}
	return nil
}

func verifyImpl(ctx context.Context, db *database.TiDBConnection, deep bool) error {
	// Schema registry + critical columns
	if err := bootstrap.VerifySchemaReadiness(db); err != nil {
		return fmt.Errorf("schema readiness: %w", err)
	}

	// All assertions (14 general + 2 managed drift)
	if _, err := bootstrap.RunAssertions(db, true); err != nil {
		return fmt.Errorf("assertions: %w", err)
	}

	// Per-pack verification (runtime-required only)
	for _, p := range packs.RuntimeRequiredPacks() {
		if err := p.Verify(ctx, packs.VerifyDeps{DB: db.DB(), DeepChecks: deep}); err != nil {
			return fmt.Errorf("pack %s verify: %w", p.Name(), err)
		}
	}

	// Report-only drift between standard_objects_spec.json and live
	// _System_Field. Non-fatal by design — apply does NOT auto-heal.
	//
	// Platform Verify is tenant-agnostic by contract: tenant drift is
	// the tenant overlay's concern and runs as part of
	// controlplane.ApplyTenant (which is itself opt-in via
	// NEXUSCRM_APPLY_TENANT_OVERLAY). Calling a tenant drift check
	// unconditionally here would regress greenfield boots, where tenant
	// spec fields are absent by design.
	if err := VerifyStandardFieldSpecDrift(db.DB()); err != nil {
		return fmt.Errorf("standard-field drift check: %w", err)
	}

	// Fatal: orphan field rows (ObjectId pointing to non-existent object)
	// are strictly broken state. Failing verify here triggers the existing
	// entrypoint.sh verify→apply fallback, which reconciles via
	// bootstrap.ReconcileOrphanFieldObjectIds.
	if err := VerifyOrphanSystemFields(db.DB()); err != nil {
		return fmt.Errorf("orphan field check: %w", err)
	}

	// Deep/operator-only: fail if profile-owned PermissionSets are missing the
	// business-object ObjectPermissions / FieldPermissions coverage implied by
	// current metadata. This is intentionally not part of pod-readiness because
	// the recommended recovery action may be `setup repair`.
	if deep {
		if err := VerifyProfilePermissionCoverage(db.DB()); err != nil {
			return fmt.Errorf("profile permission coverage check: %w", err)
		}
	}

	return nil
}
