package controllers

import (
	"log"
	"strconv"

	"git.threefold.info/herocode/heroagent/pkg/herojobs"
	"git.threefold.info/herocode/heroagent/pkg/servers/ui/models"
	"github.com/gofiber/fiber/v2"
)

// JobController handles requests related to job management
type JobController struct {
	jobManager models.JobManager
}

// NewJobController creates a new instance of JobController
func NewJobController(jobManager models.JobManager) *JobController {
	return &JobController{
		jobManager: jobManager,
	}
}

// ShowJobsPage renders the jobs management page
func (jc *JobController) ShowJobsPage(c *fiber.Ctx) error {
	// Get filter parameters
	circleID := c.Query("circle", "")
	topic := c.Query("topic", "")
	status := c.Query("status", "")

	var jobs []*models.JobInfo
	var err error

	// Apply filters
	if circleID != "" {
		jobs, err = jc.jobManager.GetJobsByCircle(circleID)
	} else if topic != "" {
		jobs, err = jc.jobManager.GetJobsByTopic(topic)
	} else if status != "" {
		// Convert status string to JobStatus
		var jobStatus herojobs.JobStatus
		switch status {
		case "new":
			jobStatus = herojobs.JobStatusNew
		case "active":
			jobStatus = herojobs.JobStatusActive
		case "error":
			jobStatus = herojobs.JobStatusError
		case "done":
			jobStatus = herojobs.JobStatusDone
		default:
			// Invalid status, get all jobs
			jobs, err = jc.jobManager.GetAllJobs()
		}

		if jobStatus != "" {
			jobs, err = jc.jobManager.GetJobsByStatus(jobStatus)
		}
	} else {
		// No filters, get all jobs
		jobs, err = jc.jobManager.GetAllJobs()
	}

	if err != nil {
		log.Printf("Error fetching jobs: %v", err)
		return c.Status(fiber.StatusInternalServerError).Render("pages/error", fiber.Map{
			"Title":   "Error",
			"Message": "Failed to fetch jobs: " + err.Error(),
		})
	}

	// Group jobs by circle and topic for tree view
	jobTree := buildJobTree(jobs)

	// Get unique circles and topics for filter dropdowns
	circles := getUniqueCircles(jobs)
	topics := getUniqueTopics(jobs)

	// Create circle options with selected state
	circleOptions := make([]map[string]interface{}, 0, len(circles))
	for _, circle := range circles {
		circleOptions = append(circleOptions, map[string]interface{}{
			"Value":    circle,
			"Text":     circle,
			"Selected": circle == circleID,
		})
	}

	// Create topic options with selected state
	topicOptions := make([]map[string]interface{}, 0, len(topics))
	for _, topicName := range topics {
		topicOptions = append(topicOptions, map[string]interface{}{
			"Value":    topicName,
			"Text":     topicName,
			"Selected": topicName == topic,
		})
	}

	// Create status options with selected state
	statusOptions := []map[string]interface{}{
		{"Value": "new", "Text": "New", "Selected": status == "new"},
		{"Value": "active", "Text": "Active", "Selected": status == "active"},
		{"Value": "done", "Text": "Done", "Selected": status == "done"},
		{"Value": "error", "Text": "Error", "Selected": status == "error"},
	}

	// Convert map options to OptionData structs
	circleOptionData := make([]OptionData, 0, len(circleOptions))
	for _, option := range circleOptions {
		circleOptionData = append(circleOptionData, OptionData{
			Value:    option["Value"].(string),
			Text:     option["Text"].(string),
			Selected: option["Selected"].(bool),
		})
	}

	topicOptionData := make([]OptionData, 0, len(topicOptions))
	for _, option := range topicOptions {
		topicOptionData = append(topicOptionData, OptionData{
			Value:    option["Value"].(string),
			Text:     option["Text"].(string),
			Selected: option["Selected"].(bool),
		})
	}

	statusOptionData := make([]OptionData, 0, len(statusOptions))
	for _, option := range statusOptions {
		statusOptionData = append(statusOptionData, OptionData{
			Value:    option["Value"].(string),
			Text:     option["Text"].(string),
			Selected: option["Selected"].(bool),
		})
	}

	// Create JobPageData struct for the template
	pageData := JobPageData{
		Title:         "Job Management",
		Jobs:          jobs,
		JobTree:       jobTree,
		CircleOptions: circleOptionData,
		TopicOptions:  topicOptionData,
		StatusOptions: statusOptionData,
		FilterCircle:  circleID,
		FilterTopic:   topic,
		FilterStatus:  status,
		TotalJobs:     len(jobs),
		ActiveJobs:    countJobsByStatus(jobs, herojobs.JobStatusActive),
		CompletedJobs: countJobsByStatus(jobs, herojobs.JobStatusDone),
		ErrorJobs:     countJobsByStatus(jobs, herojobs.JobStatusError),
		NewJobs:       countJobsByStatus(jobs, herojobs.JobStatusNew),
	}

	// Render the template with the structured data
	return c.Render("pages/jobs", pageData)
}

// ShowJobDetails renders the job details page
func (jc *JobController) ShowJobDetails(c *fiber.Ctx) error {
	// Get job ID from URL parameter
	jobIDStr := c.Params("id")
	jobID, err := strconv.ParseUint(jobIDStr, 10, 32)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).Render("pages/error", fiber.Map{
			"Title":   "Error",
			"Message": "Invalid job ID: " + jobIDStr,
		})
	}

	// Get job details
	job, err := jc.jobManager.GetJob(uint32(jobID))
	if err != nil {
		return c.Status(fiber.StatusNotFound).Render("pages/error", fiber.Map{
			"Title":   "Error",
			"Message": "Job not found: " + jobIDStr,
		})
	}

	return c.Render("pages/job_details", fiber.Map{
		"Title": "Job Details",
		"Job":   job,
	})
}

// CircleNode represents a circle in the job tree
type CircleNode struct {
	CircleID string                `json:"id"`
	Name     string                `json:"name"`
	Topics   map[string]*TopicNode `json:"topics"`
}

// TopicNode represents a topic in the job tree
type TopicNode struct {
	Topic string            `json:"topic"`
	Name  string            `json:"name"`
	Jobs  []*models.JobInfo `json:"jobs"`
}

// buildJobTree groups jobs by circle and topic for the tree view
func buildJobTree(jobs []*models.JobInfo) map[string]*CircleNode {
	tree := make(map[string]*CircleNode)

	for _, job := range jobs {
		// Get or create circle node
		circle, exists := tree[job.CircleID]
		if !exists {
			circle = &CircleNode{
				CircleID: job.CircleID,
				Name:     job.CircleID, // Use CircleID as name for now
				Topics:   make(map[string]*TopicNode),
			}
			tree[job.CircleID] = circle
		}

		// Get or create topic node
		topic, exists := circle.Topics[job.Topic]
		if !exists {
			topic = &TopicNode{
				Topic: job.Topic,
				Name:  job.Topic, // Use Topic as name for now
				Jobs:  make([]*models.JobInfo, 0),
			}
			circle.Topics[job.Topic] = topic
		}

		// Add job to topic
		topic.Jobs = append(topic.Jobs, job)
	}

	return tree
}

// getUniqueCircles returns a list of unique circle IDs from jobs
func getUniqueCircles(jobs []*models.JobInfo) []string {
	circleMap := make(map[string]bool)
	for _, job := range jobs {
		circleMap[job.CircleID] = true
	}

	circles := make([]string, 0, len(circleMap))
	for circle := range circleMap {
		circles = append(circles, circle)
	}

	return circles
}

// getUniqueTopics returns a list of unique topics from jobs
func getUniqueTopics(jobs []*models.JobInfo) []string {
	topicMap := make(map[string]bool)
	for _, job := range jobs {
		topicMap[job.Topic] = true
	}

	topics := make([]string, 0, len(topicMap))
	for topic := range topicMap {
		topics = append(topics, topic)
	}

	return topics
}

// countJobsByStatus counts jobs with a specific status
func countJobsByStatus(jobs []*models.JobInfo, status herojobs.JobStatus) int {
	count := 0
	for _, job := range jobs {
		if job.Status == status {
			count++
		}
	}
	return count
}
