package main

import (
	"time"

	iop "iop/proto/gen/iop"

	"iop/apps/control-plane/internal/wire"
)

// ── DTO structs ──────────────────────────────────────────────────────────────

type edgeRegistryResponse struct {
	Edges []edgeRegistryView `json:"edges"`
}

type edgeRegistryView struct {
	EdgeID       string    `json:"edge_id"`
	EdgeName     string    `json:"edge_name"`
	Version      string    `json:"version"`
	Capabilities []string  `json:"capabilities"`
	Protocol     string    `json:"protocol"`
	Connected    bool      `json:"connected"`
	LastSeen     time.Time `json:"last_seen"`
}

type edgeNodeEventsResponse struct {
	EdgeID string              `json:"edge_id"`
	Events []edgeNodeEventView `json:"events"`
}

type edgeNodeEventView struct {
	EdgeID     string            `json:"edge_id"`
	EventID    string            `json:"event_id"`
	Type       string            `json:"type"`
	Source     string            `json:"source"`
	NodeID     string            `json:"node_id"`
	Alias      string            `json:"alias"`
	Reason     string            `json:"reason"`
	Timestamp  time.Time         `json:"timestamp"`
	ReceivedAt time.Time         `json:"received_at"`
	Metadata   map[string]string `json:"metadata,omitempty"`
}

type edgeStatusResponseView struct {
	RequestID            string                      `json:"request_id"`
	EdgeID               string                      `json:"edge_id"`
	EdgeName             string                      `json:"edge_name"`
	ObservedTimeUnixNano int64                       `json:"observed_time_unix_nano"`
	Nodes                []edgeNodeSnapshotView      `json:"nodes"`
	Capabilities         []edgeCapabilitySummaryView `json:"capabilities,omitempty"`
	Metadata             map[string]string           `json:"metadata,omitempty"`
	Error                string                      `json:"error,omitempty"`
}

type edgeCapabilitySummaryView struct {
	Kind      string `json:"kind"`
	Available bool   `json:"available"`
	Status    string `json:"status,omitempty"`
	Summary   string `json:"summary,omitempty"`
}

// edgeCommandRequestBody is the JSON body for POST /edges/{id}/commands and
// POST /fleet/commands. It carries the surface-neutral command intent only.
type edgeCommandRequestBody struct {
	Operation      string            `json:"operation"`
	TargetSelector string            `json:"target_selector,omitempty"`
	Parameters     map[string]string `json:"parameters,omitempty"`
}

type edgeCommandResponseView struct {
	RequestID string `json:"request_id,omitempty"`
	CommandID string `json:"command_id"`
	EdgeID    string `json:"edge_id"`
	Status    string `json:"status"`
	Summary   string `json:"summary,omitempty"`
	Error     string `json:"error,omitempty"`
}

type edgeOperationsResponse struct {
	EdgeID     string                  `json:"edge_id"`
	Operations []edgeCommandRecordView `json:"operations"`
}

type edgeCommandRecordView struct {
	EdgeID         string                 `json:"edge_id"`
	CommandID      string                 `json:"command_id"`
	Operation      string                 `json:"operation,omitempty"`
	TargetSelector string                 `json:"target_selector,omitempty"`
	Status         string                 `json:"status"`
	Summary        string                 `json:"summary,omitempty"`
	Error          string                 `json:"error,omitempty"`
	CreatedAt      time.Time              `json:"created_at"`
	UpdatedAt      time.Time              `json:"updated_at"`
	Events         []edgeCommandEventView `json:"events,omitempty"`
}

type edgeCommandEventView struct {
	Phase      string    `json:"phase,omitempty"`
	Summary    string    `json:"summary,omitempty"`
	OccurredAt time.Time `json:"occurred_at,omitempty"`
	ReceivedAt time.Time `json:"received_at"`
}

type fleetStatusResponse struct {
	GeneratedAt time.Time       `json:"generated_at"`
	Edges       []fleetEdgeView `json:"edges"`
}

type fleetEdgeView struct {
	EdgeID       string                      `json:"edge_id"`
	EdgeName     string                      `json:"edge_name,omitempty"`
	Version      string                      `json:"version,omitempty"`
	Protocol     string                      `json:"protocol,omitempty"`
	Connected    bool                        `json:"connected"`
	Health       string                      `json:"health"`
	LastSeen     time.Time                   `json:"last_seen"`
	NodeCount    int                         `json:"node_count"`
	Capabilities []edgeCapabilitySummaryView `json:"capabilities,omitempty"`
	Error        string                      `json:"error,omitempty"`
}

type fleetCommandResponse struct {
	Operation string                    `json:"operation"`
	Results   []edgeCommandResponseView `json:"results"`
}

type edgeNodeSnapshotView struct {
	NodeID            string                 `json:"node_id"`
	Alias             string                 `json:"alias"`
	Label             string                 `json:"label"`
	Connected         bool                   `json:"connected"`
	Config            *nodeConfigSummaryView `json:"config,omitempty"`
	ProviderSnapshots []providerSnapshotView `json:"provider_snapshots,omitempty"`
}

type providerSnapshotView struct {
	Adapter               string   `json:"adapter,omitempty"`
	Status                string   `json:"status,omitempty"`
	Capacity              int32    `json:"capacity"`
	InFlight              int32    `json:"in_flight"`
	Queued                int32    `json:"queued"`
	LongContextCapacity   int32    `json:"long_context_capacity"`
	LongInFlight          int32    `json:"long_in_flight"`
	LongQueued            int32    `json:"long_queued"`
	ID                    string   `json:"id,omitempty"`
	Type                  string   `json:"type,omitempty"`
	Category              string   `json:"category,omitempty"`
	ServedModels          []string `json:"served_models,omitempty"`
	Health                string   `json:"health,omitempty"`
	LoadRatio             float32  `json:"load_ratio"`
	LifecycleCapabilities []string `json:"lifecycle_capabilities,omitempty"`
}

type nodeConfigSummaryView struct {
	Adapters    []adapterSummaryView `json:"adapters"`
	Concurrency int32                `json:"concurrency"`
}

type adapterSummaryView struct {
	Type    string `json:"type"`
	Enabled bool   `json:"enabled"`
}

// ── Transformation functions ─────────────────────────────────────────────────

func newNodeConfigSummaryView(payload *iop.NodeConfigPayload) *nodeConfigSummaryView {
	if payload == nil {
		return nil
	}
	var adapters []adapterSummaryView
	for _, a := range payload.GetAdapters() {
		adapters = append(adapters, adapterSummaryView{
			Type:    a.GetType(),
			Enabled: a.GetEnabled(),
		})
	}
	concurrency := int32(0)
	if payload.GetRuntime() != nil {
		concurrency = payload.GetRuntime().GetConcurrency()
	}
	return &nodeConfigSummaryView{
		Adapters:    adapters,
		Concurrency: concurrency,
	}
}

func providerSnapshotViews(snaps []*iop.ProviderSnapshot) []providerSnapshotView {
	if len(snaps) == 0 {
		return nil
	}
	views := make([]providerSnapshotView, 0, len(snaps))
	for _, p := range snaps {
		views = append(views, providerSnapshotView{
			Adapter:               p.GetAdapter(),
			Status:                p.GetStatus(),
			Capacity:              p.GetCapacity(),
			InFlight:              p.GetInFlight(),
			Queued:                p.GetQueued(),
			LongContextCapacity:   p.GetLongContextCapacity(),
			LongInFlight:          p.GetLongInFlight(),
			LongQueued:            p.GetLongQueued(),
			ID:                    p.GetId(),
			Type:                  p.GetType(),
			Category:              p.GetCategory(),
			ServedModels:          append([]string(nil), p.GetServedModels()...),
			Health:                p.GetHealth(),
			LoadRatio:             p.GetLoadRatio(),
			LifecycleCapabilities: append([]string(nil), p.GetLifecycleCapabilities()...),
		})
	}
	return views
}

func edgeRegistryViewFromState(state wire.EdgeConnectionState) edgeRegistryView {
	return edgeRegistryView{
		EdgeID:       state.EdgeID,
		EdgeName:     state.EdgeName,
		Version:      state.Version,
		Capabilities: append([]string(nil), state.Capabilities...),
		Protocol:     state.Protocol,
		Connected:    state.Connected,
		LastSeen:     state.LastSeen,
	}
}

func edgeStatusResponseViewFromProto(resp *iop.EdgeStatusResponse) edgeStatusResponseView {
	nodes := make([]edgeNodeSnapshotView, 0, len(resp.GetNodes()))
	for _, n := range resp.GetNodes() {
		nodes = append(nodes, edgeNodeSnapshotView{
			NodeID:            n.GetNodeId(),
			Alias:             n.GetAlias(),
			Label:             n.GetLabel(),
			Connected:         n.GetConnected(),
			Config:            newNodeConfigSummaryView(n.GetConfig()),
			ProviderSnapshots: providerSnapshotViews(n.GetProviderSnapshots()),
		})
	}
	return edgeStatusResponseView{
		RequestID:            resp.GetRequestId(),
		EdgeID:               resp.GetEdgeId(),
		EdgeName:             resp.GetEdgeName(),
		ObservedTimeUnixNano: resp.GetObservedTimeUnixNano(),
		Nodes:                nodes,
		Capabilities:         edgeCapabilityViews(resp.GetCapabilities()),
		Metadata:             resp.GetMetadata(),
		Error:                resp.GetError(),
	}
}

func edgeCapabilityViews(caps []*iop.EdgeCapabilitySummary) []edgeCapabilitySummaryView {
	if len(caps) == 0 {
		return nil
	}
	views := make([]edgeCapabilitySummaryView, 0, len(caps))
	for _, c := range caps {
		views = append(views, edgeCapabilitySummaryView{
			Kind:      c.GetKind(),
			Available: c.GetAvailable(),
			Status:    c.GetStatus(),
			Summary:   c.GetSummary(),
		})
	}
	return views
}

func edgeCommandResponseViewFromProto(resp *iop.EdgeCommandResponse) edgeCommandResponseView {
	return edgeCommandResponseView{
		RequestID: resp.GetRequestId(),
		CommandID: resp.GetCommandId(),
		EdgeID:    resp.GetEdgeId(),
		Status:    resp.GetStatus(),
		Summary:   resp.GetSummary(),
		Error:     resp.GetError(),
	}
}

func edgeCommandRecordViewFromRecord(record wire.EdgeCommandRecord) edgeCommandRecordView {
	view := edgeCommandRecordView{
		EdgeID:         record.EdgeID,
		CommandID:      record.CommandID,
		Operation:      record.Operation,
		TargetSelector: record.TargetSelector,
		Status:         record.Status,
		Summary:        record.Summary,
		Error:          record.Error,
		CreatedAt:      record.CreatedAt,
		UpdatedAt:      record.UpdatedAt,
	}
	if len(record.Events) > 0 {
		view.Events = make([]edgeCommandEventView, 0, len(record.Events))
		for _, e := range record.Events {
			view.Events = append(view.Events, edgeCommandEventView{
				Phase:      e.Phase,
				Summary:    e.Summary,
				OccurredAt: e.OccurredAt,
				ReceivedAt: e.ReceivedAt,
			})
		}
	}
	return view
}

func edgeNodeEventViewFromRecord(record wire.EdgeNodeEventRecord) edgeNodeEventView {
	event := record.Event
	view := edgeNodeEventView{
		EdgeID:     record.EdgeID,
		ReceivedAt: record.ReceivedAt,
	}
	if event == nil {
		return view
	}
	if event.GetTimestamp() != 0 {
		view.Timestamp = time.Unix(0, event.GetTimestamp()).UTC()
	}
	view.EventID = event.GetEventId()
	view.Type = event.GetType()
	view.Source = event.GetSource()
	view.NodeID = event.GetNodeId()
	view.Alias = event.GetAlias()
	view.Reason = event.GetReason()
	if metadata := event.GetMetadata(); len(metadata) > 0 {
		view.Metadata = make(map[string]string, len(metadata))
		for k, v := range metadata {
			view.Metadata[k] = v
		}
	}
	return view
}

func fleetEdgeViewFromState(state wire.EdgeConnectionState, requestStatus edgeStatusRequester, statusTimeout time.Duration) fleetEdgeView {
	view := fleetEdgeView{
		EdgeID:    state.EdgeID,
		EdgeName:  state.EdgeName,
		Version:   state.Version,
		Protocol:  state.Protocol,
		Connected: state.Connected,
		LastSeen:  state.LastSeen,
	}
	if !state.Connected {
		view.Health = "offline"
		return view
	}
	if requestStatus == nil {
		view.Health = "online"
		return view
	}
	resp, err := requestStatus(state.EdgeID, statusTimeout)
	if err != nil {
		view.Health = "degraded"
		view.Error = err.Error()
		return view
	}
	view.NodeCount = len(resp.GetNodes())
	view.Capabilities = edgeCapabilityViews(resp.GetCapabilities())
	if resp.GetError() != "" {
		view.Health = "degraded"
		view.Error = resp.GetError()
		return view
	}
	view.Health = "online"
	return view
}
