// Package agentv1 is the control-plane <-> node-agent gRPC contract.
//
// These types mirror server/proto/agent/v1/agent.proto 1:1. The repository has no
// protoc/buf pipeline wired yet (Makefile `generate` is a stub from task #5), so
// the messages are hand-written Go structs and the gRPC stubs (service.go) use a
// JSON codec (codec.go) instead of protobuf wire format. The JSON field names are
// the proto field names so a future protoc-generated package is drop-in
// compatible at the API surface.
//
// Data-plane red line: nothing in this package carries user_id, email, device id,
// destination address or DNS data — only the opaque dp_uuid and aggregate counters.
package agentv1

// Protocol selects which sing-box inbound(s) a credential is provisioned on.
type Protocol int32

const (
	ProtocolUnspecified Protocol = 0
	ProtocolReality     Protocol = 1
	ProtocolHy2         Protocol = 2
	ProtocolBoth        Protocol = 3
)

// CommandType discriminates the Command payload.
type CommandType int32

const (
	CommandTypeUnspecified      CommandType = 0
	CommandTypeUpsert           CommandType = 1
	CommandTypeRevoke           CommandType = 2
	CommandTypeRotateCredential CommandType = 3
	CommandTypeApplyConfig      CommandType = 4
	CommandTypeLifecycle        CommandType = 5
)

// LifecycleAction is the verb of a LIFECYCLE command.
type LifecycleAction int32

const (
	LifecycleActionUnspecified LifecycleAction = 0
	LifecycleActionDrain       LifecycleAction = 1
	LifecycleActionResume      LifecycleAction = 2
	LifecycleActionShutdown    LifecycleAction = 3
)

// ─── enroll ──────────────────────────────────────────────────────────────────

type EnrollRequest struct {
	BootstrapToken string `json:"bootstrap_token,omitempty"`
	CSRPEM         []byte `json:"csr_pem,omitempty"`
	AgentVersion   string `json:"agent_version,omitempty"`
}

type EnrollResponse struct {
	NodeUUID     string `json:"node_uuid,omitempty"`
	CertPEM      []byte `json:"cert_pem,omitempty"`
	CAPEM        []byte `json:"ca_pem,omitempty"`
	NotAfterUnix int64  `json:"not_after_unix,omitempty"`
}

// ─── register / config ─────────────────────────────────────────────────────────

type RegisterRequest struct {
	NodeUUID           string `json:"node_uuid,omitempty"`
	AgentVersion       string `json:"agent_version,omitempty"`
	LocalConfigVersion int64  `json:"local_config_version,omitempty"`
}

// Credential is the only per-subscriber object a node ever sees.
type Credential struct {
	DpUUID        string   `json:"dp_uuid,omitempty"`
	Protocol      Protocol `json:"protocol,omitempty"`
	Flow          string   `json:"flow,omitempty"`
	ExpiresAtUnix int64    `json:"expires_at_unix,omitempty"`
}

type ConfigSnapshot struct {
	ConfigVersion int64           `json:"config_version,omitempty"`
	Credentials   []*Credential   `json:"credentials,omitempty"`
	Reality       *RealityInbound `json:"reality,omitempty"`
	Hy2           *Hy2Inbound     `json:"hy2,omitempty"`
	LastCommandID int64           `json:"last_command_id,omitempty"`
}

type RealityInbound struct {
	ListenPort      int32  `json:"listen_port,omitempty"`
	PrivateKey      string `json:"private_key,omitempty"`
	ShortID         string `json:"short_id,omitempty"`
	ServerName      string `json:"server_name,omitempty"`
	HandshakeServer string `json:"handshake_server,omitempty"`
	HandshakePort   int32  `json:"handshake_port,omitempty"`
}

type Hy2Inbound struct {
	ListenPort int32  `json:"listen_port,omitempty"`
	Masquerade string `json:"masquerade,omitempty"`
	CertPath   string `json:"cert_path,omitempty"`
	KeyPath    string `json:"key_path,omitempty"`
}

// ─── heartbeat ───────────────────────────────────────────────────────────────

type HeartbeatRequest struct {
	NodeUUID         string  `json:"node_uuid,omitempty"`
	ConfigVersion    int64   `json:"config_version,omitempty"`
	OnlinePeers      int32   `json:"online_peers,omitempty"`
	BandwidthUpBps   int64   `json:"bandwidth_up_bps,omitempty"`
	BandwidthDownBps int64   `json:"bandwidth_down_bps,omitempty"`
	CPUPercent       float64 `json:"cpu_percent,omitempty"`
	TimestampUnix    int64   `json:"timestamp_unix,omitempty"`
}

type HeartbeatResponse struct {
	NeedFullResync bool  `json:"need_full_resync,omitempty"`
	ServerTimeUnix int64 `json:"server_time_unix,omitempty"`
}

// ─── command stream ──────────────────────────────────────────────────────────

type Command struct {
	CommandID   int64             `json:"command_id,omitempty"`
	Type        CommandType       `json:"type,omitempty"`
	Upsert      *UpsertPayload    `json:"upsert,omitempty"`
	Revoke      *RevokePayload    `json:"revoke,omitempty"`
	Rotate      *RotatePayload    `json:"rotate,omitempty"`
	ApplyConfig *ConfigSnapshot   `json:"apply_config,omitempty"`
	Lifecycle   *LifecyclePayload `json:"lifecycle,omitempty"`
}

type UpsertPayload struct {
	Credential *Credential `json:"credential,omitempty"`
}

type RevokePayload struct {
	DpUUID string `json:"dp_uuid,omitempty"`
}

type RotatePayload struct {
	OldDpUUID      string      `json:"old_dp_uuid,omitempty"`
	NewCredential  *Credential `json:"new_credential,omitempty"`
	GraceUntilUnix int64       `json:"grace_until_unix,omitempty"`
}

type LifecyclePayload struct {
	Action LifecycleAction `json:"action,omitempty"`
}

type SubscribeRequest struct {
	NodeUUID      string `json:"node_uuid,omitempty"`
	LastCommandID int64  `json:"last_command_id,omitempty"`
}

type AckRequest struct {
	NodeUUID  string `json:"node_uuid,omitempty"`
	CommandID int64  `json:"command_id,omitempty"`
}

type AckResponse struct{}

// ─── usage ───────────────────────────────────────────────────────────────────

type UsageEntry struct {
	DpUUID         string `json:"dp_uuid,omitempty"`
	BytesUp        int64  `json:"bytes_up,omitempty"`
	BytesDown      int64  `json:"bytes_down,omitempty"`
	SessionMinutes int64  `json:"session_minutes,omitempty"`
}

type UsageReport struct {
	NodeUUID        string        `json:"node_uuid,omitempty"`
	WindowStartUnix int64         `json:"window_start_unix,omitempty"`
	WindowEndUnix   int64         `json:"window_end_unix,omitempty"`
	Entries         []*UsageEntry `json:"entries,omitempty"`
}

type UsageAck struct{}
