package offsite

import (
	"time"

	"gitea.dooplex.hu/admin/felhom-hub/internal/store"
)

// R-70 delivery-state detector — ONE implementation, all consumers read it (customer card, the
// monitor checker's stuck event + R-71c self-heal, and any future surface). Derived from the two
// signals the hub already holds and previously read nowhere:
//   - the one_time_secrets row (exists? consumed_at?) — the delivery ledger;
//   - report offsite-object presence — the box's own testimony that an offbox target is applied
//     (the report builder attaches `offsite` only when a target is configured).
//
// Origin: DIAG-f10-demo-hp-offsite-2026-07-23 — demo-hp sat 2 days in consumed_awaiting_apply
// (the burned-credential shape) while the operator card said "Provisioned" from static copy.

// DeliveryState is the customer's offsite last-mile state.
type DeliveryState string

const (
	// DeliveryApplied — the latest report carries an offsite status object: the tier is live on
	// the box. Wins over every secret-row shape (precedence rule; an applied box with a stale
	// staged secret is APPLIED — see StaleStagedSince).
	DeliveryApplied DeliveryState = "applied"
	// DeliveryConsumedAwaitingApply — the one-time password was consumed but no report since has
	// ever shown an offbox target: the burned-credential shape (normal for seconds, wrong for
	// hours; the F10 box sat here for 2 days).
	DeliveryConsumedAwaitingApply DeliveryState = "consumed_awaiting_apply"
	// DeliveryStagedAwaitingConsume — a secret is staged and not yet consumed: delivery pending
	// (normal for minutes while the box re-pulls config, wrong for days).
	DeliveryStagedAwaitingConsume DeliveryState = "staged_awaiting_consume"
	// DeliveryNoSecret — offsite is enabled in the config but no secret row exists at all: a
	// mis-state (e.g. a RESET purged the row without deprovisioning). Rendered needs-attention.
	DeliveryNoSecret DeliveryState = "no_secret"
)

// DeliveryStatus is the derived last-mile state plus the timestamps every consumer must surface
// ("mióta" — R-70's whole point is that nothing about this plumbing stays invisible).
type DeliveryStatus struct {
	State DeliveryState
	// Since anchors the state's age: consumed_at for consumed_awaiting_apply, created_at for
	// staged_awaiting_consume, the latest report time for applied, zero for no_secret.
	Since time.Time
	// StaleStagedSince is non-zero ONLY in the applied+stale-staged edge (the demo-felhom shape):
	// the box is applied via an earlier generation while an unconsumed secret sits staged. The
	// caller renders an info line; the self-heal must NEVER touch this shape (R-39(a)).
	StaleStagedSince time.Time
	// ReportsSinceConsume / OffsiteReportsSinceConsume count reports received after consumed_at
	// (consumed_awaiting_apply only; zero otherwise). The R-71c trigger requires
	// ReportsSinceConsume >= N with OffsiteReportsSinceConsume == 0 — "consecutive without".
	ReportsSinceConsume        int
	OffsiteReportsSinceConsume int
}

// DeliveryStateFor derives the customer's offsite delivery state. Callers gate on the descriptor
// (offsite enabled) themselves — this function only reads the ledger and the reports; age math is
// the caller's (against its own clock seam).
func DeliveryStateFor(st *store.Store, customerID string) (DeliveryStatus, error) {
	found, receivedAt, hasOffsite, err := st.LatestReportOffsitePresence(customerID)
	if err != nil {
		return DeliveryStatus{}, err
	}
	secret, err := st.GetOneTimeSecretInfo(customerID)
	if err != nil {
		return DeliveryStatus{}, err
	}

	// Precedence: applied wins. The box's own report is the strongest evidence there is.
	if found && hasOffsite {
		status := DeliveryStatus{State: DeliveryApplied, Since: receivedAt}
		if secret != nil && secret.ConsumedAt.IsZero() {
			status.StaleStagedSince = secret.CreatedAt // demo-felhom shape: applied + stale staged
		}
		return status, nil
	}

	if secret == nil {
		return DeliveryStatus{State: DeliveryNoSecret}, nil
	}

	if !secret.ConsumedAt.IsZero() {
		total, withOffsite, err := st.CountReportsOffsiteSince(customerID, secret.ConsumedAt)
		if err != nil {
			return DeliveryStatus{}, err
		}
		return DeliveryStatus{
			State:                      DeliveryConsumedAwaitingApply,
			Since:                      secret.ConsumedAt,
			ReportsSinceConsume:        total,
			OffsiteReportsSinceConsume: withOffsite,
		}, nil
	}

	return DeliveryStatus{State: DeliveryStagedAwaitingConsume, Since: secret.CreatedAt}, nil
}
