package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"text/tabwriter"
	"time"
)

func runStatus(args []string) error {
	fs := flag.NewFlagSet("status", flag.ExitOnError)
	g := addGlobalFlags(fs)
	fs.Usage = func() {
		fmt.Fprint(os.Stderr, "Usage: tinyforge status [<app>]\n\nWith no app: server health and the logged-in user.\nWith an app: that app's containers.\n")
		fs.PrintDefaults()
	}
	if err := fs.Parse(args); err != nil {
		return err
	}

	sess, err := newSession(g)
	if err != nil {
		return err
	}
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	if fs.NArg() == 0 {
		return serverStatus(ctx, sess)
	}
	return appStatus(ctx, sess.client, fs.Arg(0))
}

func serverStatus(ctx context.Context, sess *session) error {
	fmt.Printf("Server:  %s\n", sess.client.baseURL)

	var me User
	if err := sess.client.doJSON(ctx, "GET", "/api/auth/me", nil, &me); err != nil {
		fmt.Printf("User:    not logged in (%v)\n", err)
	} else {
		fmt.Printf("User:    %s (%s)\n", me.Username, me.Role)
	}
	if exp := friendlyExpiry(sess.cfg.ExpiresAt); exp != "" {
		fmt.Printf("Token:   valid until %s\n", exp)
	}

	var health map[string]any
	if err := sess.client.doJSON(ctx, "GET", "/api/health", nil, &health); err != nil {
		return err
	}
	fmt.Printf("DB:      %s\n", connState(health, "database"))
	docker := connState(health, "docker")
	if v := nestedString(health, "docker", "version"); v != "" {
		docker += " (v" + v + ")"
	}
	fmt.Printf("Docker:  %s\n", docker)
	if _, ok := health["proxy"]; ok {
		fmt.Printf("Proxy:   %s\n", connState(health, "proxy"))
	}
	return nil
}

func appStatus(ctx context.Context, c *Client, ref string) error {
	app, err := resolveApp(ctx, c, ref)
	if err != nil {
		return err
	}
	var containers []Container
	if err := c.doJSON(ctx, "GET", "/api/workloads/"+app.ID+"/containers", nil, &containers); err != nil {
		return err
	}

	fmt.Printf("%s  (%s, %s)\n", app.Name, app.SourceKind, idShort(app.ID))
	if len(containers) == 0 {
		fmt.Println("No containers — not deployed yet.")
		return nil
	}

	tw := tabwriter.NewWriter(os.Stdout, 0, 2, 2, ' ', 0)
	fmt.Fprintln(tw, "ROLE\tSTATE\tIMAGE\tPORT\tSUBDOMAIN\tCONTAINER")
	for _, c := range containers {
		role := c.Role
		if role == "" {
			role = "(default)"
		}
		port := ""
		if c.Port != 0 {
			port = fmt.Sprintf("%d", c.Port)
		}
		fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\t%s\n",
			role, c.State, c.ImageRef, port, c.Subdomain, idShort(c.ID))
	}
	return tw.Flush()
}

// connState reads health[section].connected and renders connected/disconnected,
// appending the section's error string when present.
func connState(health map[string]any, section string) string {
	m, ok := health[section].(map[string]any)
	if !ok {
		return "unknown"
	}
	connected, _ := m["connected"].(bool)
	if connected {
		return "connected"
	}
	if msg, ok := m["error"].(string); ok && msg != "" {
		return "disconnected (" + msg + ")"
	}
	return "disconnected"
}

func nestedString(m map[string]any, section, key string) string {
	sub, ok := m[section].(map[string]any)
	if !ok {
		return ""
	}
	s, _ := sub[key].(string)
	return s
}
