package main

import (
	"context"
	"fmt"
	"os"
	"time"

	"github.com/nexuscrm/backend/internal/bootstrap"
	"github.com/nexuscrm/backend/internal/controlplane"
	"github.com/nexuscrm/backend/internal/infrastructure/database"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
)

func main() {
	// Initialize logging
	zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
	if os.Getenv("LOG_FORMAT") != "json" {
		log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339})
	}

	if len(os.Args) < 2 {
		usage()
		os.Exit(1)
	}

	// Connect to database
	db, err := database.GetInstance()
	if err != nil {
		log.Fatal().Err(err).Msg("Failed to connect to database")
	}
	defer func() {
		if err := db.Close(); err != nil {
			log.Warn().Err(err).Msg("Failed to close database connection")
		}
	}()

	// Install the field-provenance classifier before any operation that
	// might touch ComputeIntrinsic (BackfillFieldCapabilities in repair,
	// pack apply, etc.). Under issue #1582 RFC v1.6 Layer 1d/1e consult
	// this classifier instead of reading stored Source / IsCompound.
	if err := bootstrap.InitFieldProvenance(bootstrap.DefaultPlatformStandardFields()); err != nil {
		log.Fatal().Err(err).Msg("Failed to initialize field-provenance classifier")
	}

	ctx := context.Background()

	switch os.Args[1] {
	case "apply":
		if err := controlplane.ApplyAll(ctx, db); err != nil {
			log.Fatal().Err(err).Msg("apply failed")
		}

	case "apply-metadata":
		if _, err := controlplane.ApplyMetadata(ctx, db); err != nil {
			log.Fatal().Err(err).Msg("apply-metadata failed")
		}

	case "apply-packs":
		r, err := controlplane.NewRunner(db)
		if err != nil {
			log.Fatal().Err(err).Msg("Failed to create control-plane runner")
		}
		if err := r.RefreshCache(ctx); err != nil {
			log.Fatal().Err(err).Msg("Failed to refresh cache")
		}
		if err := controlplane.ApplyPacksWithLock(ctx, r, !isProduction()); err != nil {
			log.Fatal().Err(err).Msg("apply-packs failed")
		}

	case "apply-tenant":
		// Layers tenant-specific metadata on top of an already-applied
		// platform baseline. Invoked by deployment tooling (see
		// infra/entrypoint.sh) only when NEXUSCRM_APPLY_TENANT_OVERLAY=1.
		// Greenfield orgs never run this.
		r, err := controlplane.NewRunner(db)
		if err != nil {
			log.Fatal().Err(err).Msg("Failed to create control-plane runner")
		}
		if err := r.RefreshCache(ctx); err != nil {
			log.Fatal().Err(err).Msg("Failed to refresh cache")
		}
		if err := controlplane.ApplyTenant(ctx, r); err != nil {
			log.Fatal().Err(err).Msg("apply-tenant failed")
		}

	case "repair":
		if err := controlplane.RunRepair(ctx, db); err != nil {
			log.Fatal().Err(err).Msg("repair failed")
		}

	case "ensure-indexes":
		if err := controlplane.EnsureIndexes(ctx, db); err != nil {
			log.Fatal().Err(err).Msg("ensure-indexes failed")
		}

	case "rebuild":
		r, err := controlplane.NewRunner(db)
		if err != nil {
			log.Fatal().Err(err).Msg("Failed to create control-plane runner")
		}
		if err := r.RefreshCache(ctx); err != nil {
			log.Fatal().Err(err).Msg("Failed to refresh cache")
		}
		if err := controlplane.RebuildDerived(ctx, r); err != nil {
			log.Fatal().Err(err).Msg("rebuild failed")
		}

	case "verify":
		// Operator verify — runs the full gate set (includes
		// autonumber V5/V7 live-data scans). Assumes writers are
		// quiesced per the cmd/setup apply prerequisite; not safe on
		// a live cluster under rolling deploy load. Pod readiness
		// uses controlplane.Verify (shallow) instead.
		if err := controlplane.VerifyDeep(ctx, db); err != nil {
			log.Fatal().Err(err).Msg("verify failed")
		}
		log.Info().Msg("✅ All readiness checks passed")

	case "verify-profile":
		// Narrow operator-facing verify for the profile convergence line.
		// This intentionally surfaces only schema readiness + profile
		// permission coverage, so unrelated global verify noise does not
		// obscure the profile-specific signal.
		if err := controlplane.VerifyProfile(ctx, db); err != nil {
			log.Fatal().Err(err).Msg("verify-profile failed")
		}
		log.Info().Msg("✅ Profile convergence checks passed")

	default:
		fmt.Fprintf(os.Stderr, "unknown command: %s\n", os.Args[1])
		usage()
		os.Exit(1)
	}
}

func usage() {
	fmt.Fprintf(os.Stderr, `Usage: setup <command>

Commands:
  apply            Full control plane: metadata + repair + packs + rebuild + indexes + verify
  apply-metadata   Substrate DDL + managed metadata reconciliation only
  repair           Discovery + backfills + compatibility fixes
  apply-packs      Apply platform packs (policy, config, ux, dev-sample)
  apply-tenant     Layer tenant-specific metadata on top of the platform baseline (opt-in; skip on greenfield)
  ensure-indexes   Create standard indexes (~900, can be slow)
  rebuild          Rebuild derived state (formula views, caches)
  verify           Read-only readiness check
  verify-profile   Narrow profile convergence verify
`)
}

func isProduction() bool {
	env := os.Getenv("NEXUS_ENV")
	return env == "production" || env == "prod" || env == "staging"
}
