package runtimeworker

import (
	"context"
	"fmt"

	"github.com/nexuscrm/audit"
	"github.com/nexuscrm/backend/internal/runtimeconfig"
	"github.com/nexuscrm/backend/internal/runtimepermissionstate"
	eventfeature "github.com/nexuscrm/events"
	"github.com/nexuscrm/files"
	"github.com/nexuscrm/persistence"
	"github.com/nexuscrm/records"
)

type RepairRequest struct {
	Audit  audit.OutboxRepair
	Poison persistence.RuntimePoisonRepair
}

// RepairProduction performs one compare-and-set operator replay using the same
// deployment fence as the long-running worker. It is a short-lived job using
// the exact worker image, not an unauthenticated network control plane.
func RepairProduction(ctx context.Context, version string, config runtimeconfig.Worker, repair RepairRequest) error {
	return repairProduction(ctx, version, config, repair, runtimepermissionstate.NewGate)
}

func repairProduction(
	ctx context.Context,
	version string,
	config runtimeconfig.Worker,
	repair RepairRequest,
	permissionStateFactory permissionStateGateFactory,
) error {
	if ctx == nil {
		return fmt.Errorf("runtime worker repair: context is required")
	}
	if err := config.Role.Validate(); err != nil {
		return err
	}
	if err := validateWorkerBinary(version, config.Role); err != nil {
		return err
	}
	switch config.Role.WorkFamily {
	case audit.AuditHistoryFanoutWorkFamily:
		if config.Role.Schema == runtimeconfig.WorkerConfigSchemaV2 || config.Role.HasMetadataScope() {
			return fmt.Errorf("runtime worker repair: audit work family requires payload schema v1 or v3")
		}
	case eventfeature.RuntimeRecordEventDispatchWorkFamily, records.RuntimeRollupDeltaWorkFamily:
		if _, err := configuredMetadataWorkScope(config.Role); err != nil {
			return err
		}
		if err := repair.Poison.Validate(); err != nil {
			return err
		}
	case persistence.RuntimePlatformCommandExecutionWorkFamily:
		if _, err := configuredMetadataWorkScope(config.Role); err != nil {
			return err
		}
		if err := repair.Poison.Validate(); err != nil {
			return err
		}
	case files.RuntimeFileUploadLifecycleWorkFamily:
		if config.Role.Schema == runtimeconfig.WorkerConfigSchemaV2 || config.Role.HasMetadataScope() {
			return fmt.Errorf("runtime worker repair: file upload lifecycle requires payload schema v1 or v3")
		}
		if err := repair.Poison.Validate(); err != nil {
			return err
		}
	default:
		return fmt.Errorf("runtime worker repair: work family %q has no poison repair surface", config.Role.WorkFamily)
	}

	db, err := openWorkerDatabase(ctx, repairWorkerPool(), "runtime worker repair")
	if err != nil {
		return err
	}
	defer db.Close()
	permissionStateGate, err := newWorkerPermissionStateGate(
		ctx, db, version, config.Role, permissionStateFactory,
	)
	if err != nil {
		return fmt.Errorf("runtime worker repair: Permission State gate: %w", err)
	}
	ownership, err := persistence.NewRuntimeWorkOwnershipRepository(db, configuredWorkOwnership(config.Role))
	if err != nil {
		return err
	}
	switch config.Role.WorkFamily {
	case audit.AuditHistoryFanoutWorkFamily:
		store, err := audit.ResolveFencedOutboxStore(db, configuredWorkOwnership(config.Role))
		if err != nil {
			return err
		}
		if err := store.CheckReady(ctx); err != nil {
			return fmt.Errorf("runtime worker repair: readiness: %w", err)
		}
		if !permissionStateGate.Ready() {
			return fmt.Errorf("runtime worker repair: Permission State readiness failed")
		}
		if err := store.RepairPoison(ctx, repair.Audit); err != nil {
			return fmt.Errorf("runtime worker repair: compare-and-set replay: %w", err)
		}
	case eventfeature.RuntimeRecordEventDispatchWorkFamily:
		scope, err := configuredMetadataWorkScope(config.Role)
		if err != nil {
			return err
		}
		store, err := persistence.NewRuntimeRecordEventRepairStore(db, ownership, scope)
		if err != nil {
			return err
		}
		if err := store.CheckReady(ctx); err != nil {
			return fmt.Errorf("runtime worker repair: readiness: %w", err)
		}
		if !permissionStateGate.Ready() {
			return fmt.Errorf("runtime worker repair: Permission State readiness failed")
		}
		if err := store.Repair(ctx, repair.Poison); err != nil {
			return fmt.Errorf("runtime worker repair: compare-and-set replay: %w", err)
		}
	case records.RuntimeRollupDeltaWorkFamily:
		scope, err := configuredMetadataWorkScope(config.Role)
		if err != nil {
			return err
		}
		store, err := records.NewRuntimeRollupDeltaRepairStore(db, ownership, scope)
		if err != nil {
			return err
		}
		if err := store.CheckReady(ctx); err != nil {
			return fmt.Errorf("runtime worker repair: readiness: %w", err)
		}
		if !permissionStateGate.Ready() {
			return fmt.Errorf("runtime worker repair: Permission State readiness failed")
		}
		if err := store.Repair(ctx, repair.Poison); err != nil {
			return fmt.Errorf("runtime worker repair: compare-and-set replay: %w", err)
		}
	case files.RuntimeFileUploadLifecycleWorkFamily:
		store, err := files.NewRuntimeUploadLifecycleRepairStore(db, ownership)
		if err != nil {
			return err
		}
		if err := store.CheckReady(ctx); err != nil {
			return fmt.Errorf("runtime worker repair: readiness: %w", err)
		}
		if !permissionStateGate.Ready() {
			return fmt.Errorf("runtime worker repair: Permission State readiness failed")
		}
		if err := store.Repair(ctx, repair.Poison); err != nil {
			return fmt.Errorf("runtime worker repair: compare-and-set replay: %w", err)
		}
	case persistence.RuntimePlatformCommandExecutionWorkFamily:
		scope, err := configuredMetadataWorkScope(config.Role)
		if err != nil {
			return err
		}
		store, err := persistence.NewRuntimePlatformCommandRepairStore(
			db,
			configuredWorkOwnership(config.Role),
			scope,
		)
		if err != nil {
			return err
		}
		if err := store.CheckReady(ctx); err != nil {
			return fmt.Errorf("runtime worker repair: readiness: %w", err)
		}
		if !permissionStateGate.Ready() {
			return fmt.Errorf("runtime worker repair: Permission State readiness failed")
		}
		if err := store.RepairPoison(ctx, repair.Poison); err != nil {
			return fmt.Errorf("runtime worker repair: compare-and-set replay: %w", err)
		}
	}
	return nil
}

func configuredMetadataWorkScope(role runtimeconfig.WorkerRole) (persistence.RuntimeWorkScope, error) {
	metadataScope, err := role.MetadataScope()
	if err != nil {
		return persistence.RuntimeWorkScope{}, fmt.Errorf("runtime worker: metadata work family requires its exact scope: %w", err)
	}
	scope := persistence.RuntimeWorkScope{
		OrgID: metadataScope.OrgID, AppInstallationID: metadataScope.AppInstallationID,
		RuntimeCapabilityID: metadataScope.RuntimeCapabilityID,
	}
	if err := scope.Validate(); err != nil {
		return persistence.RuntimeWorkScope{}, err
	}
	return scope, nil
}
