package main

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"github.com/YspCoder/clawgo/pkg/config"
	"github.com/YspCoder/clawgo/pkg/nodes"
	"github.com/YspCoder/clawgo/pkg/providers"
)

func statusCmd() {
	cfg, err := loadConfig()
	if err != nil {
		fmt.Printf("Error loading config: %v\n", err)
		return
	}

	configPath := getConfigPath()

	fmt.Printf("%s clawgo Status\n\n", logo)

	if _, err := os.Stat(configPath); err == nil {
		fmt.Println("Config:", configPath, "[ok]")
	} else {
		fmt.Println("Config:", configPath, "[missing]")
	}

	workspace := cfg.WorkspacePath()
	if _, err := os.Stat(workspace); err == nil {
		fmt.Println("Workspace:", workspace, "[ok]")
	} else {
		fmt.Println("Workspace:", workspace, "[missing]")
	}

	if _, err := os.Stat(configPath); err == nil {
		activeProviderName, activeModel := config.ParseProviderModelRef(cfg.Agents.Defaults.Model.Primary)
		if activeProviderName == "" {
			activeProviderName = config.PrimaryProviderName(cfg)
		}
		activeProvider, _ := config.ProviderConfigByName(cfg, activeProviderName)
		fmt.Printf("Primary Model: %s\n", activeModel)
		fmt.Printf("Primary Provider: %s\n", activeProviderName)
		fmt.Printf("Provider Base URL: %s\n", activeProvider.APIBase)
		fmt.Printf("Responses Compact: %v\n", providers.ProviderSupportsResponsesCompact(cfg, activeProviderName))
		hasKey := strings.TrimSpace(activeProvider.APIKey) != ""
		status := "not set"
		if hasKey {
			status = "configured"
		}
		fmt.Printf("API Key Status: %s\n", status)
		fmt.Printf("Logging: %v\n", cfg.Logging.Enabled)
		if cfg.Logging.Enabled {
			fmt.Printf("Log File: %s\n", cfg.LogFilePath())
			fmt.Printf("Log Max Size: %d MB\n", cfg.Logging.MaxSizeMB)
			fmt.Printf("Log Retention: %d days\n", cfg.Logging.RetentionDays)
		}

		fmt.Printf("Heartbeat: enabled=%v interval=%ds ackMaxChars=%d\n",
			cfg.Agents.Defaults.Heartbeat.Enabled,
			cfg.Agents.Defaults.Heartbeat.EverySec,
			cfg.Agents.Defaults.Heartbeat.AckMaxChars,
		)
		fmt.Printf("Cron Runtime: workers=%d sleep=%d-%ds\n",
			cfg.Cron.MaxWorkers,
			cfg.Cron.MinSleepSec,
			cfg.Cron.MaxSleepSec,
		)

		heartbeatLog := filepath.Join(workspace, "memory", "heartbeat.log")
		if data, err := os.ReadFile(heartbeatLog); err == nil {
			trimmed := strings.TrimSpace(string(data))
			if trimmed != "" {
				lines := strings.Split(trimmed, "\n")
				fmt.Printf("Heartbeat Runs Logged: %d\n", len(lines))
				fmt.Printf("Heartbeat Last Log: %s\n", lines[len(lines)-1])
			}
		}

		triggerStats := filepath.Join(workspace, "memory", "trigger-stats.json")
		if data, err := os.ReadFile(triggerStats); err == nil {
			fmt.Printf("Trigger Stats: %s\n", strings.TrimSpace(string(data)))
		}
		auditPath := filepath.Join(workspace, "memory", "trigger-audit.jsonl")
		if errs, err := collectRecentTriggerErrors(auditPath, 5); err == nil && len(errs) > 0 {
			fmt.Println("Recent Trigger Errors:")
			for _, e := range errs {
				fmt.Printf("  - %s\n", e)
			}
		}
		if agg, err := collectTriggerErrorCounts(auditPath); err == nil && len(agg) > 0 {
			fmt.Println("Trigger Error Counts:")
			keys := make([]string, 0, len(agg))
			for k := range agg {
				keys = append(keys, k)
			}
			sort.Strings(keys)
			for _, trigger := range keys {
				fmt.Printf("  %s: %d\n", trigger, agg[trigger])
			}
		}
		if total, okCnt, failCnt, reasonCov, top, err := collectSkillExecStats(filepath.Join(workspace, "memory", "skill-audit.jsonl")); err == nil && total > 0 {
			fmt.Printf("Skill Exec: total=%d ok=%d fail=%d reason_coverage=%.2f\n", total, okCnt, failCnt, reasonCov)
			if top != "" {
				fmt.Printf("Skill Exec Top: %s\n", top)
			}
		}

		sessionsDir := filepath.Join(filepath.Dir(configPath), "sessions")
		if kinds, err := collectSessionKindCounts(sessionsDir); err == nil && len(kinds) > 0 {
			fmt.Println("Session Kinds:")
			for _, k := range []string{"main", "cron", "subagent", "hook", "node", "other"} {
				if v, ok := kinds[k]; ok {
					fmt.Printf("  %s: %d\n", k, v)
				}
			}
		}
		if recent, err := collectRecentSubagentSessions(sessionsDir, 5); err == nil && len(recent) > 0 {
			fmt.Println("Recent Subagent Sessions:")
			for _, key := range recent {
				fmt.Printf("  - %s\n", key)
			}
		}
		fmt.Printf("Nodes P2P: enabled=%t transport=%s\n", cfg.Gateway.Nodes.P2P.Enabled, strings.TrimSpace(cfg.Gateway.Nodes.P2P.Transport))
		fmt.Printf("Nodes P2P ICE: stun=%d ice=%d\n", len(cfg.Gateway.Nodes.P2P.STUNServers), len(cfg.Gateway.Nodes.P2P.ICEServers))
		ns := nodes.DefaultManager().List()
		if len(ns) > 0 {
			online := 0
			caps := map[string]int{"run": 0, "model": 0, "camera": 0, "screen": 0, "location": 0, "canvas": 0}
			for _, n := range ns {
				if n.Online {
					online++
				}
				if n.Capabilities.Run {
					caps["run"]++
				}
				if n.Capabilities.Model {
					caps["model"]++
				}
				if n.Capabilities.Camera {
					caps["camera"]++
				}
				if n.Capabilities.Screen {
					caps["screen"]++
				}
				if n.Capabilities.Location {
					caps["location"]++
				}
				if n.Capabilities.Canvas {
					caps["canvas"]++
				}
			}
			fmt.Printf("Nodes: total=%d online=%d\n", len(ns), online)
			fmt.Printf("Nodes Capabilities: run=%d model=%d camera=%d screen=%d location=%d canvas=%d\n", caps["run"], caps["model"], caps["camera"], caps["screen"], caps["location"], caps["canvas"])
			if total, okCnt, avgMs, actionTop, transportTop, fallbackCnt, err := collectNodeDispatchStats(filepath.Join(workspace, "memory", "nodes-dispatch-audit.jsonl")); err == nil && total > 0 {
				fmt.Printf("Nodes Dispatch: total=%d ok=%d fail=%d avg_ms=%d\n", total, okCnt, total-okCnt, avgMs)
				if actionTop != "" {
					fmt.Printf("Nodes Dispatch Top Action: %s\n", actionTop)
				}
				if transportTop != "" {
					fmt.Printf("Nodes Dispatch Top Transport: %s\n", transportTop)
				}
				if fallbackCnt > 0 {
					fmt.Printf("Nodes Dispatch Fallbacks: %d\n", fallbackCnt)
				}
			}
		}
	}
}

func collectSessionKindCounts(sessionsDir string) (map[string]int, error) {
	indexPath := filepath.Join(sessionsDir, "sessions.json")
	data, err := os.ReadFile(indexPath)
	if err != nil {
		return nil, err
	}
	var index map[string]struct {
		Kind string `json:"kind"`
	}
	if err := json.Unmarshal(data, &index); err != nil {
		return nil, err
	}
	counts := map[string]int{}
	for _, row := range index {
		kind := strings.TrimSpace(strings.ToLower(row.Kind))
		if kind == "" {
			kind = "other"
		}
		counts[kind]++
	}
	return counts, nil
}

func collectRecentTriggerErrors(path string, limit int) ([]string, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, err
	}
	lines := strings.Split(strings.TrimSpace(string(data)), "\n")
	if len(lines) == 1 && strings.TrimSpace(lines[0]) == "" {
		return nil, nil
	}
	out := make([]string, 0, limit)
	for i := len(lines) - 1; i >= 0; i-- {
		line := strings.TrimSpace(lines[i])
		if line == "" {
			continue
		}
		var row struct {
			Time    string `json:"time"`
			Trigger string `json:"trigger"`
			Error   string `json:"error"`
		}
		if err := json.Unmarshal([]byte(line), &row); err != nil {
			continue
		}
		if strings.TrimSpace(row.Error) == "" {
			continue
		}
		out = append(out, fmt.Sprintf("[%s/%s] %s", row.Time, row.Trigger, row.Error))
		if len(out) >= limit {
			break
		}
	}
	return out, nil
}

func collectTriggerErrorCounts(path string) (map[string]int, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, err
	}
	lines := strings.Split(strings.TrimSpace(string(data)), "\n")
	counts := map[string]int{}
	for _, line := range lines {
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		}
		var row struct {
			Trigger string `json:"trigger"`
			Error   string `json:"error"`
		}
		if err := json.Unmarshal([]byte(line), &row); err != nil {
			continue
		}
		if strings.TrimSpace(row.Error) == "" {
			continue
		}
		trigger := strings.ToLower(strings.TrimSpace(row.Trigger))
		if trigger == "" {
			trigger = "unknown"
		}
		counts[trigger]++
	}
	return counts, nil
}

func collectNodeDispatchStats(path string) (int, int, int, string, string, int, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return 0, 0, 0, "", "", 0, err
	}
	lines := strings.Split(strings.TrimSpace(string(data)), "\n")
	total, okCnt, msSum, fallbackCnt := 0, 0, 0, 0
	actionCnt := map[string]int{}
	transportCnt := map[string]int{}
	for _, line := range lines {
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		}
		var row struct {
			Action        string `json:"action"`
			UsedTransport string `json:"used_transport"`
			FallbackFrom  string `json:"fallback_from"`
			OK            bool   `json:"ok"`
			DurationMS    int    `json:"duration_ms"`
		}
		if err := json.Unmarshal([]byte(line), &row); err != nil {
			continue
		}
		total++
		if row.OK {
			okCnt++
		}
		if row.DurationMS > 0 {
			msSum += row.DurationMS
		}
		a := strings.TrimSpace(strings.ToLower(row.Action))
		if a == "" {
			a = "unknown"
		}
		actionCnt[a]++
		used := strings.TrimSpace(strings.ToLower(row.UsedTransport))
		if used != "" {
			transportCnt[used]++
		}
		if strings.TrimSpace(row.FallbackFrom) != "" {
			fallbackCnt++
		}
	}
	avg := 0
	if total > 0 {
		avg = msSum / total
	}
	topAction := ""
	topN := 0
	for k, v := range actionCnt {
		if v > topN {
			topN = v
			topAction = k
		}
	}
	if topAction != "" {
		topAction = fmt.Sprintf("%s(%d)", topAction, topN)
	}
	topTransport := ""
	topTN := 0
	for k, v := range transportCnt {
		if v > topTN {
			topTN = v
			topTransport = k
		}
	}
	if topTransport != "" {
		topTransport = fmt.Sprintf("%s(%d)", topTransport, topTN)
	}
	return total, okCnt, avg, topAction, topTransport, fallbackCnt, nil
}

func collectSkillExecStats(path string) (int, int, int, float64, string, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return 0, 0, 0, 0, "", err
	}
	lines := strings.Split(strings.TrimSpace(string(data)), "\n")
	total, okCnt, failCnt := 0, 0, 0
	reasonCnt := 0
	skillCounts := map[string]int{}
	for _, line := range lines {
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		}
		var row struct {
			Skill  string `json:"skill"`
			Reason string `json:"reason"`
			OK     bool   `json:"ok"`
		}
		if err := json.Unmarshal([]byte(line), &row); err != nil {
			continue
		}
		total++
		if row.OK {
			okCnt++
		} else {
			failCnt++
		}
		r := strings.TrimSpace(strings.ToLower(row.Reason))
		if r != "" && r != "unspecified" {
			reasonCnt++
		}
		s := strings.TrimSpace(row.Skill)
		if s == "" {
			s = "unknown"
		}
		skillCounts[s]++
	}
	topSkill := ""
	topN := 0
	for k, v := range skillCounts {
		if v > topN {
			topN = v
			topSkill = k
		}
	}
	if topSkill != "" {
		topSkill = fmt.Sprintf("%s(%d)", topSkill, topN)
	}
	reasonCoverage := 0.0
	if total > 0 {
		reasonCoverage = float64(reasonCnt) / float64(total)
	}
	return total, okCnt, failCnt, reasonCoverage, topSkill, nil
}

func collectRecentSubagentSessions(sessionsDir string, limit int) ([]string, error) {
	indexPath := filepath.Join(sessionsDir, "sessions.json")
	data, err := os.ReadFile(indexPath)
	if err != nil {
		return nil, err
	}
	var index map[string]struct {
		Kind      string `json:"kind"`
		UpdatedAt int64  `json:"updatedAt"`
	}
	if err := json.Unmarshal(data, &index); err != nil {
		return nil, err
	}
	type item struct {
		key     string
		updated int64
	}
	items := make([]item, 0)
	for key, row := range index {
		if strings.ToLower(strings.TrimSpace(row.Kind)) != "subagent" {
			continue
		}
		items = append(items, item{key: key, updated: row.UpdatedAt})
	}
	sort.Slice(items, func(i, j int) bool { return items[i].updated > items[j].updated })
	if limit > 0 && len(items) > limit {
		items = items[:limit]
	}
	out := make([]string, 0, len(items))
	for _, it := range items {
		out = append(out, it.key)
	}
	return out, nil
}
