package output

import (
	"fmt"
	"io"
	"net"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/pranshuparmar/witr/pkg/model"
)

// Maximum number of items to display in any list before truncating
const MaxDisplayItems = 10

var detailLabels = map[string]string{
	"type":      "              Type",
	"plist":     "              Plist",
	"triggers":  "              Trigger",
	"keepalive": "              KeepAlive",
}

func formatDetailLabel(key string) string {
	if label, ok := detailLabels[key]; ok {
		return label
	}
	return "              " + key
}

func RenderWarnings(w io.Writer, r model.Result, colorEnabled bool) {
	out := NewPrinter(w)

	proc := r.Process
	if len(r.Ancestry) > 0 {
		proc = r.Ancestry[len(r.Ancestry)-1]
	}

	proc.Command = SanitizeTerminal(proc.Command)

	if colorEnabled {
		out.Printf("%sProcess%s     : %s%s%s (%spid %d%s)\n", ColorBlue, ColorReset, ColorGreen, proc.Command, ColorReset, ColorDim, proc.PID, ColorReset)
		if proc.Cmdline != "" {
			out.Printf("%sCommand%s     : %s\n", ColorBlue, ColorReset, proc.Cmdline)
		} else {
			out.Printf("%sCommand%s     : %s\n", ColorBlue, ColorReset, proc.Command)
		}
	} else {
		out.Printf("Process     : %s (pid %d)\n", proc.Command, proc.PID)
		if proc.Cmdline != "" {
			out.Printf("Command     : %s\n", proc.Cmdline)
		} else {
			out.Printf("Command     : %s\n", proc.Command)
		}
	}

	if len(r.Warnings) == 0 {
		if colorEnabled {
			out.Printf("%sWarnings%s    : %sNo warnings.%s\n", ColorRed, ColorReset, ColorGreen, ColorReset)
		} else {
			out.Println("Warnings    : No warnings.")
		}
		return
	}

	if colorEnabled {
		out.Printf("%sWarnings%s    :\n", ColorRed, ColorReset)
		for _, w := range r.Warnings {
			out.Printf("  • %s\n", SanitizeTerminal(w))
		}
	} else {
		out.Println("Warnings    :")
		for _, w := range r.Warnings {
			out.Printf("  • %s\n", SanitizeTerminal(w))
		}
	}
}

func RenderStandard(w io.Writer, r model.Result, colorEnabled bool, verbose bool) {
	out := NewPrinter(w)
	if len(r.Ancestry) == 0 {
		out.Println("No process information available.")
		return
	}

	target := SanitizeTerminal(r.Ancestry[len(r.Ancestry)-1].Command)
	if colorEnabled {
		out.Printf("%sTarget%s      : %s\n\n", ColorBlue, ColorReset, target)
	} else {
		out.Printf("Target      : %s\n\n", target)
	}

	var proc = r.Ancestry[len(r.Ancestry)-1]
	proc.Command = SanitizeTerminal(proc.Command)
	proc.Cmdline = SanitizeTerminal(proc.Cmdline)
	proc.User = SanitizeTerminal(proc.User)
	proc.Container = SanitizeTerminal(proc.Container)
	proc.Service = SanitizeTerminal(proc.Service)
	proc.WorkingDir = SanitizeTerminal(proc.WorkingDir)
	proc.GitRepo = SanitizeTerminal(proc.GitRepo)
	proc.GitBranch = SanitizeTerminal(proc.GitBranch)
	if colorEnabled {
		out.Printf("%sProcess%s     : %s%s%s (%spid %d%s)", ColorBlue, ColorReset, ColorGreen, proc.Command, ColorReset, ColorDim, proc.PID, ColorReset)
	} else {
		out.Printf("Process     : %s (pid %d)", proc.Command, proc.PID)
	}
	// Health status
	if proc.Health != "" && proc.Health != "healthy" {
		health := SanitizeTerminal(proc.Health)
		healthColor := ColorRed
		if colorEnabled {
			out.Printf(" %s[%s]%s", healthColor, health, ColorReset)
		} else {
			out.Printf(" [%s]", health)
		}
	}
	// Forked status: only display if forked
	if proc.Forked == "forked" {
		forkColor := ColorDimYellow
		if colorEnabled {
			out.Printf(" %s{forked}%s", forkColor, ColorReset)
		} else {
			out.Printf(" {forked}")
		}
	}
	out.Println("")
	if proc.User != "" && proc.User != "unknown" {
		if colorEnabled {
			out.Printf("%sUser%s        : %s\n", ColorBlue, ColorReset, proc.User)
		} else {
			out.Printf("User        : %s\n", proc.User)
		}
	}

	// Container
	if proc.Container != "" {
		if colorEnabled {
			out.Printf("%sContainer%s   : %s\n", ColorBlue, ColorReset, proc.Container)
		} else {
			out.Printf("Container   : %s\n", proc.Container)
		}
	}
	// Service
	if proc.Service != "" {
		if colorEnabled {
			out.Printf("%sService%s     : %s\n", ColorBlue, ColorReset, proc.Service)
		} else {
			out.Printf("Service     : %s\n", proc.Service)
		}
	}

	if proc.Cmdline != "" {
		if colorEnabled {
			out.Printf("%sCommand%s     : %s\n", ColorBlue, ColorReset, proc.Cmdline)
		} else {
			out.Printf("Command     : %s\n", proc.Cmdline)
		}
	} else {
		if colorEnabled {
			out.Printf("%sCommand%s     : %s\n", ColorBlue, ColorReset, proc.Command)
		} else {
			out.Printf("Command     : %s\n", proc.Command)
		}
	}
	// Format as: 2 days ago (Mon 2025-02-02 11:42:10 +0530)
	startedAt := proc.StartedAt
	now := time.Now()
	dur := now.Sub(startedAt)
	var rel string
	switch {
	case dur.Hours() >= 48:
		days := int(dur.Hours()) / 24
		rel = fmt.Sprintf("%d days ago", days)
	case dur.Hours() >= 24:
		rel = "1 day ago"
	case dur.Hours() >= 2:
		hours := int(dur.Hours())
		rel = fmt.Sprintf("%d hours ago", hours)
	case dur.Minutes() >= 60:
		rel = "1 hour ago"
	default:
		mins := int(dur.Minutes())
		if mins > 0 {
			rel = fmt.Sprintf("%d min ago", mins)
		} else {
			rel = "just now"
		}
	}
	dtStr := startedAt.Format("Mon 2006-01-02 15:04:05 -07:00")
	if colorEnabled {
		out.Printf("%sStarted%s     : %s (%s)\n", ColorMagenta, ColorReset, rel, dtStr)
	} else {
		out.Printf("Started     : %s (%s)\n", rel, dtStr)
	}

	if schedule, ok := r.Source.Details["schedule"]; ok {
		if colorEnabled {
			out.Printf("%sSchedule%s    : %s\n", ColorMagenta, ColorReset, schedule)
		} else {
			out.Printf("Schedule    : %s\n", schedule)
		}
	}

	// Why It Exists (short chain)
	if colorEnabled {
		out.Printf("\n%sWhy It Exists%s :\n  ", ColorMagenta, ColorReset)
		for i, p := range r.Ancestry {
			name := p.Command
			if name == "" && p.Cmdline != "" {
				name = p.Cmdline
			}
			name = SanitizeTerminal(name)

			nameColor := ansiString("")
			if i == len(r.Ancestry)-1 {
				nameColor = ColorGreen
			}
			out.Printf("%s%s%s (%spid %d%s)", nameColor, name, ColorReset, ColorDim, p.PID, ColorReset)
			if i < len(r.Ancestry)-1 {
				out.Printf(" %s\u2192%s ", ColorMagenta, ColorReset)
			}
		}
		out.Print("\n\n")
	} else {
		out.Printf("\nWhy It Exists :\n  ")
		for i, p := range r.Ancestry {
			name := p.Command
			if name == "" && p.Cmdline != "" {
				name = p.Cmdline
			}
			name = SanitizeTerminal(name)
			out.Printf("%s (pid %d)", name, p.PID)
			if i < len(r.Ancestry)-1 {
				out.Printf(" \u2192 ")
			}
		}
		out.Print("\n\n")
	}

	// Source
	sourceLabel := string(r.Source.Type)
	sourceName := SanitizeTerminal(r.Source.Name)
	if colorEnabled {
		if r.Source.Name != "" && r.Source.Name != sourceLabel {
			out.Printf("%sSource%s      : %s (%s)\n", ColorCyan, ColorReset, sourceName, sourceLabel)
		} else {
			out.Printf("%sSource%s      : %s\n", ColorCyan, ColorReset, sourceLabel)
		}
	} else {
		if r.Source.Name != "" && r.Source.Name != sourceLabel {
			out.Printf("Source      : %s (%s)\n", sourceName, sourceLabel)
		} else {
			out.Printf("Source      : %s\n", sourceLabel)
		}
	}

	// Description
	if r.Source.Description != "" {
		if colorEnabled {
			out.Printf("%sDescription%s : %s\n", ColorCyan, ColorReset, SanitizeTerminal(r.Source.Description))
		} else {
			out.Printf("Description : %s\n", SanitizeTerminal(r.Source.Description))
		}
	}

	// Unit File / Config Source
	if r.Source.UnitFile != "" {
		label := "Unit File"
		switch r.Source.Type {
		case model.SourceLaunchd:
			label = "Plist File"
		case model.SourceWindowsService:
			label = "Registry Key"
		case model.SourceBsdRc:
			label = "Rc Script"
		}

		var pad string
		if len(label) < 12 {
			pad = strings.Repeat(" ", 12-len(label))
		} else {
			pad = " "
		}

		if colorEnabled {
			out.Printf("%s%s%s%s: %s\n", ColorCyan, label, ColorReset, pad, r.Source.UnitFile)
		} else {
			out.Printf("%s%s: %s\n", label, pad, r.Source.UnitFile)
		}
	}

	// Source details (launchd triggers, plist path, etc.)
	if len(r.Source.Details) > 0 {
		// Display in consistent order
		detailKeys := []string{"type", "plist", "triggers", "keepalive"}
		for _, key := range detailKeys {
			if val, ok := r.Source.Details[key]; ok {
				label := formatDetailLabel(key)
				if colorEnabled {
					out.Printf("%s%s%s : %s\n", ColorDim, label, ColorReset, SanitizeTerminal(val))
				} else {
					out.Printf("%s : %s\n", label, SanitizeTerminal(val))
				}
			}
		}
	}

	// Context group
	if colorEnabled {
		if proc.WorkingDir != "" && proc.WorkingDir != "unknown" {
			out.Printf("\n%sWorking Dir%s : %s\n", ColorCyan, ColorReset, proc.WorkingDir)
		}
		if proc.GitRepo != "" {
			if proc.GitBranch != "" {
				out.Printf("%sGit Repo%s    : %s (%s)\n", ColorCyan, ColorReset, proc.GitRepo, proc.GitBranch)
			} else {
				out.Printf("%sGit Repo%s    : %s\n", ColorCyan, ColorReset, proc.GitRepo)
			}
		}
	} else {
		if proc.WorkingDir != "" && proc.WorkingDir != "unknown" {
			out.Printf("\nWorking Dir : %s\n", proc.WorkingDir)
		}
		if proc.GitRepo != "" {
			if proc.GitBranch != "" {
				out.Printf("Git Repo    : %s (%s)\n", proc.GitRepo, proc.GitBranch)
			} else {
				out.Printf("Git Repo    : %s\n", proc.GitRepo)
			}
		}
	}

	// Listening section (address:port)
	if len(proc.ListeningPorts) > 0 && len(proc.BindAddresses) == len(proc.ListeningPorts) {
		count := len(proc.ListeningPorts)
		displayed := 0
		for i := range proc.ListeningPorts {
			if displayed >= MaxDisplayItems {
				remaining := count - displayed
				out.Printf("              ... and %d more\n", remaining)
				break
			}
			addr := proc.BindAddresses[i]
			port := proc.ListeningPorts[i]
			if addr != "" && port > 0 {
				hostPort := net.JoinHostPort(addr, strconv.Itoa(port))
				safeHostPort := SanitizeTerminal(hostPort)
				if colorEnabled {
					if i == 0 {
						out.Printf("%sListening%s   : %s\n", ColorGreen, ColorReset, safeHostPort)
					} else {
						out.Printf("              %s\n", safeHostPort)
					}
				} else {
					if i == 0 {
						out.Printf("Listening   : %s\n", safeHostPort)
					} else {
						out.Printf("              %s\n", safeHostPort)
					}
				}
				displayed++
			}
		}
	}

	// Warnings
	if len(r.Warnings) > 0 {
		if colorEnabled {
			out.Printf("\n%sWarnings%s    :\n", ColorRed, ColorReset)
			for _, w := range r.Warnings {
				out.Printf("  • %s\n", SanitizeTerminal(w))
			}
		} else {
			out.Println("\nWarnings    :")
			for _, w := range r.Warnings {
				out.Printf("  • %s\n", SanitizeTerminal(w))
			}
		}
	}

	// Extended information for verbose mode
	if verbose {
		out.Println()
		// Resource context (thermal state, sleep prevention, CPU)
		if r.ResourceContext != nil {
			if colorEnabled {
				if r.ResourceContext.CPUUsage > 70 {
					out.Printf("%sCPU%s         : %s%.1f%%%s\n", ColorRed, ColorReset, ColorDimYellow, r.ResourceContext.CPUUsage, ColorReset)
				} else {
					out.Printf("%sCPU%s         : %.1f%%\n", ColorGreen, ColorReset, r.ResourceContext.CPUUsage)
				}
			} else {
				out.Printf("CPU         : %.1f%%\n", r.ResourceContext.CPUUsage)
			}

			if r.ResourceContext.PreventsSleep {
				if colorEnabled {
					out.Printf("%sEnergy%s      : %sPreventing system sleep%s\n", ColorRed, ColorReset, ColorDimYellow, ColorReset)
				} else {
					out.Printf("Energy      : Preventing system sleep\n")
				}
			}

			if r.ResourceContext.ThermalState != "" {
				thermalState := SanitizeTerminal(r.ResourceContext.ThermalState)
				if colorEnabled {
					out.Printf("%sThermal%s     : %s%s%s\n", ColorRed, ColorReset, ColorDimYellow, thermalState, ColorReset)
				} else {
					out.Printf("Thermal     : %s\n", thermalState)
				}
			}
		}

		// Memory information
		if proc.Memory.VMS > 0 {
			if colorEnabled {
				out.Printf("\n%sMemory%s:\n", ColorGreen, ColorReset)
				out.Printf("  Virtual  : %.1f MB\n", proc.Memory.VMSMB)
				out.Printf("  Resident : %.1f MB\n", proc.Memory.RSSMB)
				if r.ResourceContext != nil && r.ResourceContext.MemoryUsage > 0 {
					out.Printf("  Private  : %.1f MB\n", float64(r.ResourceContext.MemoryUsage)/(1024*1024))
				}
				if proc.Memory.Shared > 0 {
					out.Printf("  Shared   : %.1f MB\n", float64(proc.Memory.Shared)/(1024*1024))
				}
			} else {
				out.Printf("\nMemory:\n")
				out.Printf("  Virtual  : %.1f MB\n", proc.Memory.VMSMB)
				out.Printf("  Resident : %.1f MB\n", proc.Memory.RSSMB)
				if r.ResourceContext != nil && r.ResourceContext.MemoryUsage > 0 {
					out.Printf("  Private  : %.1f MB\n", float64(r.ResourceContext.MemoryUsage)/(1024*1024))
				}
				if proc.Memory.Shared > 0 {
					out.Printf("  Shared   : %.1f MB\n", float64(proc.Memory.Shared)/(1024*1024))
				}
			}
		}

		// I/O statistics
		if proc.IO.ReadBytes > 0 || proc.IO.WriteBytes > 0 {
			if colorEnabled {
				out.Printf("\n%sI/O Statistics%s:\n", ColorGreen, ColorReset)
				if proc.IO.ReadBytes > 0 {
					out.Printf("  Read  : %.1f MB (%d ops)\n", float64(proc.IO.ReadBytes)/(1024*1024), proc.IO.ReadOps)
				}
				if proc.IO.WriteBytes > 0 {
					out.Printf("  Write : %.1f MB (%d ops)\n", float64(proc.IO.WriteBytes)/(1024*1024), proc.IO.WriteOps)
				}
			} else {
				out.Printf("\nI/O Statistics:\n")
				if proc.IO.ReadBytes > 0 {
					out.Printf("  Read  : %.1f MB (%d ops)\n", float64(proc.IO.ReadBytes)/(1024*1024), proc.IO.ReadOps)
				}
				if proc.IO.WriteBytes > 0 {
					out.Printf("  Write : %.1f MB (%d ops)\n", float64(proc.IO.WriteBytes)/(1024*1024), proc.IO.WriteOps)
				}
			}
		}

		// File context (open files, locks)
		if r.FileContext != nil {
			if r.FileContext.OpenFiles > 0 && r.FileContext.FileLimit == 0 {
				if colorEnabled {
					out.Printf("\n%sOpen Files%s  : %d of unlimited\n", ColorGreen, ColorReset, r.FileContext.OpenFiles)
				} else {
					out.Printf("\nOpen Files  : %d of unlimited\n", r.FileContext.OpenFiles)
				}
			}
			if r.FileContext.OpenFiles > 0 && r.FileContext.FileLimit > 0 {
				usagePercent := float64(r.FileContext.OpenFiles) / float64(r.FileContext.FileLimit) * 100
				if colorEnabled {
					if usagePercent > 80 {
						out.Printf("\n%sOpen Files%s  : %s%d of %d (%.0f%%)%s\n", ColorRed, ColorReset, ColorDimYellow, r.FileContext.OpenFiles, r.FileContext.FileLimit, usagePercent, ColorReset)
					} else {
						out.Printf("\n%sOpen Files%s  : %d of %d (%.0f%%)\n", ColorGreen, ColorReset, r.FileContext.OpenFiles, r.FileContext.FileLimit, usagePercent)
					}
				} else {
					out.Printf("\nOpen Files  : %d of %d (%.0f%%)\n", r.FileContext.OpenFiles, r.FileContext.FileLimit, usagePercent)
				}
			}
			if len(r.FileContext.LockedFiles) > 0 {
				count := len(r.FileContext.LockedFiles)
				firstLocked := SanitizeTerminal(r.FileContext.LockedFiles[0])

				if colorEnabled {
					out.Printf("%sLocks%s       : %s\n", ColorGreen, ColorReset, firstLocked)
				} else {
					out.Printf("Locks       : %s\n", firstLocked)
				}

				for i, f := range r.FileContext.LockedFiles[1:] {
					if 1+i >= MaxDisplayItems {
						remaining := count - (1 + i)
						out.Printf("              ... and %d more\n", remaining)
						break
					}
					out.Printf("              %s\n", SanitizeTerminal(f))
				}
			}
		}

		// File descriptors
		if proc.FDCount > 0 {
			// Sort file descriptors numerically
			sort.Slice(proc.FileDescs, func(i, j int) bool {
				fdI := proc.FileDescs[i]
				fdJ := proc.FileDescs[j]
				idxI := strings.Index(fdI, " ")
				idxJ := strings.Index(fdJ, " ")

				if idxI == -1 || idxJ == -1 {
					return fdI < fdJ
				}

				numI, errI := strconv.Atoi(fdI[:idxI])
				numJ, errJ := strconv.Atoi(fdJ[:idxJ])

				if errI == nil && errJ == nil {
					return numI < numJ
				}
				return fdI < fdJ
			})

			if colorEnabled {
				if proc.FDLimit == 0 {
					out.Printf("\n%sFile Descriptors%s: %d/unlimited\n", ColorGreen, ColorReset, proc.FDCount)
				} else {
					out.Printf("\n%sFile Descriptors%s: %d/%d\n", ColorGreen, ColorReset, proc.FDCount, proc.FDLimit)
				}
				if len(proc.FileDescs) > 0 && len(proc.FileDescs) <= MaxDisplayItems {
					for _, fd := range proc.FileDescs {
						safeFd := SanitizeTerminal(fd)
						safeFd = strings.Replace(safeFd, "->", string(ColorMagenta)+"->"+string(ColorReset), 1)
						out.Printf("  %s\n", ansiString(safeFd))
					}
				} else if len(proc.FileDescs) > MaxDisplayItems {
					out.Printf("  Showing first %d of %d descriptors:\n", MaxDisplayItems, len(proc.FileDescs))
					for i := 0; i < MaxDisplayItems; i++ {
						safeFd := SanitizeTerminal(proc.FileDescs[i])
						safeFd = strings.Replace(safeFd, "->", string(ColorMagenta)+"->"+string(ColorReset), 1)
						out.Printf("  %s\n", ansiString(safeFd))
					}
					out.Printf("  ... and %d more\n", len(proc.FileDescs)-MaxDisplayItems)
				}
			} else {
				if proc.FDLimit == 0 {
					out.Printf("\nFile Descriptors: %d/unlimited\n", proc.FDCount)
				} else {
					out.Printf("\nFile Descriptors: %d/%d\n", proc.FDCount, proc.FDLimit)
				}
				if len(proc.FileDescs) > 0 && len(proc.FileDescs) <= MaxDisplayItems {
					for _, fd := range proc.FileDescs {
						out.Printf("  %s\n", SanitizeTerminal(fd))
					}
				} else if len(proc.FileDescs) > MaxDisplayItems {
					out.Printf("  Showing first %d of %d descriptors:\n", MaxDisplayItems, len(proc.FileDescs))
					for i := 0; i < MaxDisplayItems; i++ {
						out.Printf("  %s\n", SanitizeTerminal(proc.FileDescs[i]))
					}
					out.Printf("  ... and %d more\n", len(proc.FileDescs)-MaxDisplayItems)
				}
			}
		}

		// Socket state (for port queries)
		if r.SocketInfo != nil {
			state := SanitizeTerminal(r.SocketInfo.State)
			explanation := SanitizeTerminal(r.SocketInfo.Explanation)
			workaround := SanitizeTerminal(r.SocketInfo.Workaround)
			if colorEnabled {
				out.Printf("%sSocket%s      : %s\n", ColorGreen, ColorReset, state)
				if explanation != "" {
					out.Printf("              %s\n", explanation)
				}
				if workaround != "" {
					out.Printf("              %s%s%s\n", ColorDimYellow, workaround, ColorReset)
				}
			} else {
				out.Printf("Socket      : %s\n", state)
				if explanation != "" {
					out.Printf("              %s\n", explanation)
				}
				if workaround != "" {
					out.Printf("              %s\n", workaround)
				}
			}
		}

		// Threads
		if proc.ThreadCount > 1 {
			if colorEnabled {
				out.Printf("\n%sThreads%s: %d\n", ColorGreen, ColorReset, proc.ThreadCount)
			} else {
				out.Printf("\nThreads: %d\n", proc.ThreadCount)
			}
		}

		// Child processes
		if len(r.Children) > 0 {
			out.Println("")
			PrintChildren(w, r.Process, r.Children, colorEnabled)
		}
	}
}
