package api

import (
	"strings"
	"time"

	storeagent "github.com/tidbcloud/AgentTeam/internal/store/agent"
)

// AgentRuntimeOverlay is the daemon-owned live runtime state used to overlay
// already-authorized durable agent rows. Implementations must not perform DB
// reads or widen the result set being overlaid.
type AgentRuntimeOverlay interface {
	MachineConnected(machineID string) bool
	AgentRuntimeRunning(machineID string, agentID string) bool
	AgentRuntimeSessionID(machineID string, agentID string) (string, bool)
}

type agentRuntimeStatusOverlay interface {
	AgentRuntimeStatus(machineID string, agentID string) (status string, activity string, detail string, ok bool)
}

// OverlayAppAgentsRuntime applies daemon-owned live state to browser-facing
// agent rows that have already been selected by a durable, bounded query.
func OverlayAppAgentsRuntime(agents []storeagent.AppAgent, runtime AgentRuntimeOverlay, now time.Time) {
	for index := range agents {
		OverlayAppAgentRuntime(&agents[index], runtime, now)
	}
}

// OverlayAppAgentRuntime applies daemon-owned live state to one browser-facing
// agent row. The row remains in the caller's durable result set; this helper
// only replaces live status/session fields.
func OverlayAppAgentRuntime(agent *storeagent.AppAgent, runtime AgentRuntimeOverlay, now time.Time) {
	if agent == nil {
		return
	}
	if !agent.CanManage {
		defer redactAgentRuntimeForNonOwner(&agent.Status, &agent.Activity, &agent.ActivityDetail, &agent.CurrentInstance)
	}
	if runtime == nil {
		return
	}
	machineID := strings.TrimSpace(agent.MachineID)
	agentID := strings.TrimSpace(agent.ID)
	if machineID == "" || agentID == "" {
		return
	}
	if !runtime.MachineConnected(machineID) {
		agent.MachineStatus = "offline"
		agent.Status = "inactive"
		agent.Activity = "offline"
		agent.ActivityDetail = ""
		agent.CurrentInstance = nil
		return
	}

	agent.MachineStatus = "online"
	status, activity, detail, hasStatus := runtimeStatus(runtime, machineID, agentID)
	running := runtime.AgentRuntimeRunning(machineID, agentID)
	sessionID, hasSession := runtime.AgentRuntimeSessionID(machineID, agentID)
	active := running || hasSession || runtimeStatusActive(status, activity)

	if hasStatus {
		overlayAgentStatusFields(&agent.Status, &agent.Activity, &agent.ActivityDetail, status, activity, detail)
	}
	if !active {
		agent.CurrentInstance = nil
		return
	}

	agent.Status = "active"
	if strings.TrimSpace(agent.Activity) == "" || strings.TrimSpace(agent.Activity) == "offline" {
		agent.Activity = "online"
	}
	if !hasSession {
		agent.CurrentInstance = nil
		return
	}
	trimmedSessionID := strings.TrimSpace(sessionID)
	if trimmedSessionID == "" {
		agent.CurrentInstance = nil
		return
	}
	agent.CurrentInstance = &storeagent.AppAgentInstance{
		ID:             "local:" + agentID + ":" + trimmedSessionID,
		MachineID:      machineID,
		SessionID:      trimmedSessionID,
		Status:         agent.Status,
		Activity:       agent.Activity,
		ActivityDetail: agent.ActivityDetail,
		StartedAt:      now,
		LastSeenAt:     now,
	}
}

// OverlayAgentRuntimeSnapshots applies daemon-owned live state to bounded
// runtime snapshot rows.
func OverlayAgentRuntimeSnapshots(snapshots []storeagent.AgentRuntimeSnapshot, runtime AgentRuntimeOverlay, now time.Time) {
	for index := range snapshots {
		OverlayAgentRuntimeSnapshot(&snapshots[index], runtime, now)
	}
}

// OverlayAgentRuntimeSnapshot applies daemon-owned live state to one bounded
// runtime snapshot row.
func OverlayAgentRuntimeSnapshot(snapshot *storeagent.AgentRuntimeSnapshot, runtime AgentRuntimeOverlay, now time.Time) {
	if snapshot == nil {
		return
	}
	if !snapshot.CanManage {
		defer redactAgentRuntimeForNonOwner(&snapshot.Status, &snapshot.Activity, &snapshot.ActivityDetail, &snapshot.CurrentInstance)
	}
	if runtime == nil {
		return
	}
	machineID := strings.TrimSpace(snapshot.MachineID)
	agentID := strings.TrimSpace(snapshot.AgentID)
	if machineID == "" || agentID == "" {
		return
	}
	if !runtime.MachineConnected(machineID) {
		snapshot.MachineStatus = "offline"
		snapshot.Status = "inactive"
		snapshot.Activity = "offline"
		snapshot.ActivityDetail = ""
		snapshot.CurrentInstance = nil
		return
	}

	snapshot.MachineStatus = "online"
	status, activity, detail, hasStatus := runtimeStatus(runtime, machineID, agentID)
	running := runtime.AgentRuntimeRunning(machineID, agentID)
	sessionID, hasSession := runtime.AgentRuntimeSessionID(machineID, agentID)
	active := running || hasSession || runtimeStatusActive(status, activity)

	if hasStatus {
		overlayAgentStatusFields(&snapshot.Status, &snapshot.Activity, &snapshot.ActivityDetail, status, activity, detail)
	}
	if !active {
		snapshot.CurrentInstance = nil
		return
	}

	snapshot.Status = "active"
	if strings.TrimSpace(snapshot.Activity) == "" || strings.TrimSpace(snapshot.Activity) == "offline" {
		snapshot.Activity = "online"
	}
	if !hasSession {
		snapshot.CurrentInstance = nil
		return
	}
	trimmedSessionID := strings.TrimSpace(sessionID)
	if trimmedSessionID == "" {
		snapshot.CurrentInstance = nil
		return
	}
	snapshot.CurrentInstance = &storeagent.AppAgentInstance{
		ID:             "local:" + agentID + ":" + trimmedSessionID,
		MachineID:      machineID,
		SessionID:      trimmedSessionID,
		Status:         snapshot.Status,
		Activity:       snapshot.Activity,
		ActivityDetail: snapshot.ActivityDetail,
		StartedAt:      now,
		LastSeenAt:     now,
	}
}

func redactAgentRuntimeForNonOwner(status *string, activity *string, detail *string, currentInstance **storeagent.AppAgentInstance) {
	*detail = ""
	*currentInstance = nil
	if strings.TrimSpace(*status) == "active" {
		*activity = "online"
		return
	}
	*activity = "offline"
}

// OverlayWorkspaceActorsRuntime applies daemon-owned live state to the agent
// rows already present in an agent-facing workspace snapshot.
func OverlayWorkspaceActorsRuntime(actors []storeagent.WorkspaceActor, runtime AgentRuntimeOverlay) {
	for index := range actors {
		OverlayWorkspaceActorRuntime(&actors[index], runtime)
	}
}

// OverlayWorkspaceActorRuntime applies daemon-owned live state to one
// agent-facing workspace actor row. Actor.ID should contain the durable agent
// id; Actor.Name is used only as a compatibility fallback.
func OverlayWorkspaceActorRuntime(actor *storeagent.WorkspaceActor, runtime AgentRuntimeOverlay) {
	if actor == nil || runtime == nil {
		return
	}
	machineID := strings.TrimSpace(actor.MachineID)
	agentID := strings.TrimSpace(actor.ID)
	if agentID == "" {
		agentID = strings.TrimSpace(actor.Name)
	}
	if machineID == "" || agentID == "" {
		return
	}
	if !runtime.MachineConnected(machineID) {
		actor.Status = "offline"
		actor.Activity = "offline"
		actor.ActivityDetail = ""
		return
	}

	status, activity, detail, hasStatus := runtimeStatus(runtime, machineID, agentID)
	running := runtime.AgentRuntimeRunning(machineID, agentID)
	_, hasSession := runtime.AgentRuntimeSessionID(machineID, agentID)
	active := running || hasSession || runtimeStatusActive(status, activity)

	if hasStatus {
		overlayAgentStatusFields(&status, &actor.Activity, &actor.ActivityDetail, status, activity, detail)
	}
	if active {
		if strings.TrimSpace(actor.Activity) == "" || strings.TrimSpace(actor.Activity) == "offline" {
			actor.Activity = "online"
		}
		actor.Status = workspacePresenceStatus("active", actor.Activity, true)
		return
	}
	if hasStatus {
		actor.Status = workspacePresenceStatus(status, actor.Activity, true)
		return
	}
	if strings.TrimSpace(actor.Status) == "offline" {
		actor.Status = workspacePresenceStatus("inactive", actor.Activity, true)
	}
}

func runtimeStatus(runtime AgentRuntimeOverlay, machineID string, agentID string) (string, string, string, bool) {
	withStatus, ok := runtime.(agentRuntimeStatusOverlay)
	if !ok {
		return "", "", "", false
	}
	status, activity, detail, found := withStatus.AgentRuntimeStatus(machineID, agentID)
	if !found {
		return "", "", "", false
	}
	return strings.TrimSpace(status), strings.TrimSpace(activity), strings.TrimSpace(detail), true
}

func overlayAgentStatusFields(currentStatus *string, currentActivity *string, currentDetail *string, status string, activity string, detail string) {
	if trimmed := strings.TrimSpace(status); trimmed != "" {
		*currentStatus = trimmed
	}
	if trimmed := strings.TrimSpace(activity); trimmed != "" {
		*currentActivity = trimmed
		*currentDetail = strings.TrimSpace(detail)
	}
}

func runtimeStatusActive(status string, activity string) bool {
	switch strings.TrimSpace(status) {
	case "active", "starting":
		return true
	}
	switch strings.TrimSpace(activity) {
	case "", "offline", "sleeping":
		return false
	default:
		return true
	}
}

func workspacePresenceStatus(agentStatus string, activity string, machineOnline bool) string {
	if !machineOnline {
		return "offline"
	}
	switch strings.TrimSpace(agentStatus) {
	case "inactive":
		return "idle"
	case "sleeping":
		return "sleeping"
	}
	switch strings.TrimSpace(activity) {
	case "working", "thinking":
		return "busy"
	case "sleeping":
		return "sleeping"
	default:
		return "online"
	}
}
