package jobs

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"time"

	pb "github.com/ehsaniara/joblet-proto/v2/gen"
	"github.com/ehsaniara/joblet/internal/rnx/common"

	"github.com/spf13/cobra"
)

func NewStatusCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "status <uuid>",
		Short: "Get comprehensive status and details of a job by UUID",
		Long: `Get comprehensive status and details of a job by UUID.

The status command shows complete job information including:
• Job identification (UUID, command, arguments)
• Execution status and timing (created, started, ended, duration)
• Resource limits (CPU, memory, I/O, core binding, GPU allocation)
• Runtime environment (Python, Java, Node.js runtimes)
• Network configuration (bridge, isolated, custom networks)
• Volume storage (mounted persistent and memory volumes)
• Environment variables (regular and secret/masked)
• File uploads and working directory
• Process results (exit code, completion status)
• Contextual next actions (view logs, stop job, etc.)

Jobs use UUIDs (36-character identifiers).
Short-form UUIDs are supported - you can use just the first 8 characters
if they uniquely identify a job.

Examples:
  # Get comprehensive job status (using full UUID)
  rnx job status f47ac10b-58cc-4372-a567-0e02b2c3d479

  # Get job status (using short-form UUID)
  rnx job status f47ac10b

  # Get job status in JSON format (all fields)
  rnx job status --json f47ac10b

Job Status Information Displayed:
  • Basic Info: Job UUID, command with arguments, current status
  • Timing: Creation time, start time, end time, execution duration
  • Resource Limits: CPU percentage, memory MB, I/O bandwidth, CPU cores, GPUs
  • Runtime Environment: Python, Java, Node.js runtime specifications
  • Network: Network configuration (bridge, isolated, custom networks)
  • Storage: Mounted volumes (filesystem and memory-based)
  • Working Directory: Job execution directory path
  • Uploaded Files: List of files uploaded for job execution
  • Environment: Regular environment variables (visible in logs)
  • Secrets: Secret environment variables (masked as ***)
  • Results: Exit code and completion status
  • Actions: Contextual next steps (view logs, stop job, etc.)

Output Formats:
  • Default: Readable formatted output with sections
  • --json: Machine-readable JSON with all available fields`,
		Args: cobra.ExactArgs(1),
		RunE: runStatus,
	}

	return cmd
}

func runStatus(cmd *cobra.Command, args []string) error {
	id := args[0]
	return getJobStatus(id)
}

func getJobStatus(jobID string) error {
	jobClient, err := common.NewJobClient()
	if err != nil {
		return fmt.Errorf("couldn't connect to joblet server: %w", err)
	}
	defer jobClient.Close()

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	response, err := jobClient.GetJobStatus(ctx, jobID)
	if err != nil {
		return fmt.Errorf("couldn't get job status: %v", err)
	}

	if common.JSONOutput {
		return outputJobStatusJSON(response)
	}

	// Display basic job information
	fmt.Printf("Job ID: %s\n", response.Uuid)
	if response.NodeId != "" {
		fmt.Printf("Node ID: %s\n", response.NodeId)
	}
	fmt.Printf("Command: %s %s\n", response.Command, strings.Join(response.Args, " "))

	// Display status with color coding (if terminal supports it)
	statusColor, resetColor := getStatusColor(response.Status)
	fmt.Printf("Status: %s%s%s\n", statusColor, response.Status, resetColor)

	// Display scheduling information if available
	if response.ScheduledTime != "" {
		fmt.Printf("\nScheduling Information:\n")

		scheduledTime, err := time.Parse("2006-01-02T15:04:05Z07:00", response.ScheduledTime)
		if err == nil {
			fmt.Printf("  Scheduled Time: %s\n", scheduledTime.Format("2006-01-02 15:04:05 MST"))

			// Show time until execution for scheduled jobs
			if response.Status == "SCHEDULED" {
				now := time.Now()
				if scheduledTime.After(now) {
					duration := scheduledTime.Sub(now)
					fmt.Printf("  Time Until Execution: %s\n", formatDuration(duration))
				} else {
					fmt.Printf("  Time Until Execution: Due now (waiting for execution)\n")
				}
			}
		} else {
			fmt.Printf("  Scheduled Time: %s\n", response.ScheduledTime)
		}
	}

	// Display timing information
	fmt.Printf("\nTiming:\n")
	fmt.Printf("  Created: %s\n", formatTimestamp(response.StartTime))

	if response.EndTime != "" {
		fmt.Printf("  Ended: %s\n", formatTimestamp(response.EndTime))

		// Calculate duration for completed jobs
		startTime, startErr := time.Parse("2006-01-02T15:04:05Z07:00", response.StartTime)
		endTime, endErr := time.Parse("2006-01-02T15:04:05Z07:00", response.EndTime)
		if startErr == nil && endErr == nil {
			duration := endTime.Sub(startTime)
			fmt.Printf("  Duration: %s\n", formatDuration(duration))
		}
	} else if response.Status == "RUNNING" {
		startTime, err := time.Parse("2006-01-02T15:04:05Z07:00", response.StartTime)
		if err == nil {
			duration := time.Since(startTime)
			fmt.Printf("  Running For: %s\n", formatDuration(duration))
		}
	}

	// Display resource limits (only show non-default/requested limits)
	hasResourceLimits := false
	resourceLimits := []string{}

	if response.MaxCpu > 0 {
		resourceLimits = append(resourceLimits, fmt.Sprintf("  Max CPU: %d%%", response.MaxCpu))
		hasResourceLimits = true
	}
	if response.MaxMemory > 0 {
		resourceLimits = append(resourceLimits, fmt.Sprintf("  Max Memory: %d MB", response.MaxMemory))
		hasResourceLimits = true
	}
	if response.MaxIoBps > 0 {
		resourceLimits = append(resourceLimits, fmt.Sprintf("  Max IO BPS: %d", response.MaxIoBps))
		hasResourceLimits = true
	}
	if response.CpuCores != "" {
		resourceLimits = append(resourceLimits, fmt.Sprintf("  CPU Cores: %s", response.CpuCores))
		hasResourceLimits = true
	}

	// Add GPU resource information if available
	if response.GpuCount > 0 {
		if len(response.GpuIndices) > 0 {
			// Show allocated GPUs
			gpuIndicesStr := make([]string, len(response.GpuIndices))
			for i, idx := range response.GpuIndices {
				gpuIndicesStr[i] = fmt.Sprintf("%d", idx)
			}
			resourceLimits = append(resourceLimits, fmt.Sprintf("  GPUs: %d allocated (indices: %s)",
				response.GpuCount, strings.Join(gpuIndicesStr, ", ")))
		} else {
			// Show requested but not yet allocated
			resourceLimits = append(resourceLimits, fmt.Sprintf("  GPUs: %d requested", response.GpuCount))
		}
		if response.GpuMemoryMb > 0 {
			resourceLimits = append(resourceLimits, fmt.Sprintf("  GPU Memory: %d MB", response.GpuMemoryMb))
		}
		hasResourceLimits = true
	}

	if hasResourceLimits {
		fmt.Printf("\nResource Limits:\n")
		for _, limit := range resourceLimits {
			fmt.Printf("%s\n", limit)
		}
	}

	// Display runtime information
	if response.Runtime != "" {
		fmt.Printf("\nRuntime Environment:\n")
		fmt.Printf("  Runtime: %s\n", response.Runtime)
	}

	// Display network configuration
	if response.Network != "" {
		fmt.Printf("\nNetwork Configuration:\n")
		fmt.Printf("  Network: %s\n", response.Network)
	}

	// Display volume information
	if len(response.Volumes) > 0 {
		fmt.Printf("\nVolumes:\n")
		for _, volume := range response.Volumes {
			fmt.Printf("  - %s\n", volume)
		}
	}

	// Display working directory
	if response.WorkDir != "" {
		fmt.Printf("\nWorking Directory:\n")
		fmt.Printf("  Work Dir: %s\n", response.WorkDir)
	}

	// Display uploaded files
	if len(response.Uploads) > 0 {
		fmt.Printf("\nUploaded Files:\n")
		for _, upload := range response.Uploads {
			fmt.Printf("  - %s\n", upload)
		}
	}

	// Display environment variables (if any)
	hasEnvVars := len(response.Environment) > 0 || len(response.SecretEnvironment) > 0
	if hasEnvVars {
		fmt.Printf("\nEnvironment Variables:\n")

		// Display regular environment variables
		for key, value := range response.Environment {
			fmt.Printf("  %s=%s\n", key, value)
		}

		// Display secret environment variables (masked)
		for key, maskedValue := range response.SecretEnvironment {
			fmt.Printf("  %s=%s (secret)\n", key, maskedValue)
		}
	}

	// Display exit code for completed jobs
	if response.Status != "RUNNING" && response.Status != "SCHEDULED" && response.Status != "INITIALIZING" {
		fmt.Printf("\nResult:\n")
		fmt.Printf("  Exit Code: %d\n", response.ExitCode)
	}

	// Provide helpful next steps based on job status
	fmt.Printf("\nAvailable Actions:\n")
	switch response.Status {
	case "SCHEDULED":
		fmt.Printf("  • rnx job stop %s     # Cancel scheduled job\n", response.Uuid)
		fmt.Printf("  • rnx job status %s   # Check status again\n", response.Uuid)
	case "RUNNING":
		fmt.Printf("  • rnx job log %s      # Stream live logs\n", response.Uuid)
		fmt.Printf("  • rnx job stop %s     # Stop running job\n", response.Uuid)
	case "COMPLETED", "FAILED", "STOPPED":
		fmt.Printf("  • rnx job log %s      # View job logs\n", response.Uuid)
	default:
		fmt.Printf("  • rnx job log %s      # View job logs\n", response.Uuid)
		fmt.Printf("  • rnx job stop %s     # Stop job if running\n", response.Uuid)
	}

	return nil
}

// formatTimestamp formats a timestamp string for display
func formatTimestamp(timestamp string) string {
	if timestamp == "" {
		return "Not available"
	}

	t, err := time.Parse("2006-01-02T15:04:05Z07:00", timestamp)
	if err != nil {
		return timestamp // Return as-is if parsing fails
	}

	return t.Format("2006-01-02 15:04:05 MST")
}

// formatDuration formats a duration for readable display

func formatDuration(d time.Duration) string {
	if d < time.Minute {
		return fmt.Sprintf("%.1fs", d.Seconds())
	} else if d < time.Hour {
		return fmt.Sprintf("%.1fm", d.Minutes())
	} else if d < 24*time.Hour {
		hours := int(d.Hours())
		minutes := int(d.Minutes()) % 60
		if minutes == 0 {
			return fmt.Sprintf("%dh", hours)
		}
		return fmt.Sprintf("%dh%dm", hours, minutes)
	} else {
		days := int(d.Hours()) / 24
		hours := int(d.Hours()) % 24
		if hours == 0 {
			return fmt.Sprintf("%dd", days)
		}
		return fmt.Sprintf("%dd%dh", days, hours)
	}
}

// outputJobStatusJSON outputs the job status in JSON format
func outputJobStatusJSON(response *pb.GetJobStatusResponse) error {
	// Create a structured output that includes all fields, even when empty
	output := map[string]interface{}{
		"uuid":              response.Uuid,
		"nodeId":            response.NodeId,
		"command":           response.Command,
		"args":              response.Args,
		"status":            response.Status,
		"startTime":         response.StartTime,
		"endTime":           response.EndTime,
		"exitCode":          response.ExitCode,
		"scheduledTime":     response.ScheduledTime,
		"environment":       response.Environment,
		"secretEnvironment": response.SecretEnvironment,
		"network":           response.Network,
		"volumes":           response.Volumes,
		"runtime":           response.Runtime,
		"workDir":           response.WorkDir,
		"uploads":           response.Uploads,
	}

	// Include resource limits if set
	if response.MaxCpu > 0 {
		output["maxCPU"] = response.MaxCpu
	}
	if response.MaxMemory > 0 {
		output["maxMemory"] = response.MaxMemory
	}
	if response.MaxIoBps > 0 {
		output["maxIOBPS"] = response.MaxIoBps
	}
	if response.CpuCores != "" {
		output["cpuCores"] = response.CpuCores
	}

	// Include GPU fields if set
	if response.GpuCount > 0 {
		output["gpuCount"] = response.GpuCount
	}
	if response.GpuMemoryMb > 0 {
		output["gpuMemoryMb"] = response.GpuMemoryMb
	}
	if len(response.GpuIndices) > 0 {
		output["gpuIndices"] = response.GpuIndices
	}

	encoder := json.NewEncoder(os.Stdout)
	encoder.SetIndent("", "  ")
	return encoder.Encode(output)
}
