package http

import (
	stdhttp "net/http"

	"gitea.dcglab.co.uk/steve/restic-manager/internal/store"
	"gitea.dcglab.co.uk/steve/restic-manager/internal/version"
)

// hostView is the JSON projection of a Host row. Same shape as the
// store row, but with explicit time-strings so wire format is stable
// across DB driver changes.
type hostView struct {
	ID               string   `json:"id"`
	Name             string   `json:"name"`
	OS               string   `json:"os"`
	Arch             string   `json:"arch"`
	AgentVersion     string   `json:"agent_version,omitempty"`
	ResticVersion    string   `json:"restic_version,omitempty"`
	ProtocolVersion  int      `json:"protocol_version"`
	EnrolledAt       string   `json:"enrolled_at"`
	LastSeenAt       *string  `json:"last_seen_at,omitempty"`
	Status           string   `json:"status"`
	Tags             []string `json:"tags"`
	CurrentJobID     *string  `json:"current_job_id,omitempty"`
	LastBackupAt     *string  `json:"last_backup_at,omitempty"`
	LastBackupStatus *string  `json:"last_backup_status,omitempty"`
	RepoStatus       string   `json:"repo_status,omitempty"`
	RepoSizeBytes    int64    `json:"repo_size_bytes"`
	SnapshotCount    int      `json:"snapshot_count"`
	OpenAlertCount   int      `json:"open_alert_count"`
	UpdateAvailable  bool     `json:"update_available"`
	TargetVersion    string   `json:"target_version,omitempty"`
}

// handleListHosts returns the full fleet as JSON. Authenticated; the
// shape mirrors what the dashboard renders, so external API consumers
// see the same projection.
func (s *Server) handleListHosts(w stdhttp.ResponseWriter, r *stdhttp.Request) {
	if !s.authedUser(r) {
		writeJSONError(w, stdhttp.StatusUnauthorized, "unauthorised", "")
		return
	}
	hosts, err := s.deps.Store.ListHosts(r.Context())
	if err != nil {
		writeJSONError(w, stdhttp.StatusInternalServerError, "internal", "")
		return
	}
	out := make([]hostView, 0, len(hosts))
	for _, h := range hosts {
		out = append(out, hostToView(h))
	}
	writeJSON(w, stdhttp.StatusOK, map[string]any{
		"count": len(out),
		"hosts": out,
	})
}

// handleFleetSummary returns the dashboard tile aggregate.
func (s *Server) handleFleetSummary(w stdhttp.ResponseWriter, r *stdhttp.Request) {
	if !s.authedUser(r) {
		writeJSONError(w, stdhttp.StatusUnauthorized, "unauthorised", "")
		return
	}
	fs, err := s.deps.Store.FleetSummary(r.Context())
	if err != nil {
		writeJSONError(w, stdhttp.StatusInternalServerError, "internal", "")
		return
	}
	writeJSON(w, stdhttp.StatusOK, fs)
}

// hostToView converts a store.Host to its JSON shape. Times become
// RFC 3339 strings; nullable times are passed through as nil.
func hostToView(h store.Host) hostView {
	v := hostView{
		ID:               h.ID,
		Name:             h.Name,
		OS:               h.OS,
		Arch:             h.Arch,
		AgentVersion:     h.AgentVersion,
		ResticVersion:    h.ResticVersion,
		ProtocolVersion:  h.ProtocolVersion,
		EnrolledAt:       h.EnrolledAt.Format("2006-01-02T15:04:05.000000000Z"),
		Status:           h.Status,
		Tags:             h.Tags,
		CurrentJobID:     h.CurrentJobID,
		LastBackupStatus: h.LastBackupStatus,
		RepoStatus:       h.RepoStatus,
		RepoSizeBytes:    h.RepoSizeBytes,
		SnapshotCount:    h.SnapshotCount,
		OpenAlertCount:   h.OpenAlertCount,
		TargetVersion:    version.Version,
		UpdateAvailable:  h.AgentVersion != "" && h.AgentVersion != version.Version,
	}
	if v.Tags == nil {
		v.Tags = []string{}
	}
	if h.LastSeenAt != nil {
		s := h.LastSeenAt.Format("2006-01-02T15:04:05.000000000Z")
		v.LastSeenAt = &s
	}
	if h.LastBackupAt != nil {
		s := h.LastBackupAt.Format("2006-01-02T15:04:05.000000000Z")
		v.LastBackupAt = &s
	}
	return v
}
