// Package services provides TUI service implementations
package services

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/jfraeys/fetch_ml/cmd/tui/internal/config"
	"github.com/jfraeys/fetch_ml/cmd/tui/internal/model"
	"github.com/jfraeys/fetch_ml/internal/domain"
	"github.com/jfraeys/fetch_ml/internal/experiment"
	"github.com/jfraeys/fetch_ml/internal/network"
	"github.com/jfraeys/fetch_ml/internal/queue"
)

// Task is an alias for domain.Task for TUI compatibility
type Task = domain.Task

// TaskQueue provides TUI-specific task operations by embedding queue.TaskQueue
// and extending it with experiment management capabilities.
type TaskQueue struct {
	*queue.TaskQueue // Embed to inherit all queue methods directly
	expManager       *experiment.Manager
	ctx              context.Context
	config           *config.Config
}

// NewTaskQueue creates a new task queue service
func NewTaskQueue(cfg *config.Config) (*TaskQueue, error) {
	// Create internal queue config
	queueCfg := queue.Config{
		RedisAddr:     cfg.RedisAddr,
		RedisPassword: cfg.RedisPassword,
		RedisDB:       cfg.RedisDB,
	}

	internalQueue, err := queue.NewTaskQueue(queueCfg)
	if err != nil {
		return nil, fmt.Errorf("failed to create task queue: %w", err)
	}

	// Initialize experiment manager with proper path
	// BasePath already includes the mode-based experiments path (e.g., ./data/dev/experiments)
	expDir := cfg.BasePath
	os.MkdirAll(expDir, 0755)
	expManager := experiment.NewManager(expDir)

	return &TaskQueue{
		TaskQueue:  internalQueue,
		expManager: expManager,
		ctx:        context.Background(),
		config:     cfg,
	}, nil
}

// EnqueueTask adds a new task to the queue (TUI-specific: creates task with proper defaults)
func (tq *TaskQueue) EnqueueTask(jobName, args string, priority int64) (*Task, error) {
	// Create internal task
	internalTask := &queue.Task{
		JobName:  jobName,
		Args:     args,
		Priority: priority,
	}

	// Use embedded queue to enqueue
	err := tq.TaskQueue.AddTask(internalTask)
	if err != nil {
		return nil, err
	}

	// Return domain.Task directly (no conversion needed)
	return internalTask, nil
}

// GetQueuedTasks retrieves all queued tasks (TUI-specific alias for GetAllTasks)
func (tq *TaskQueue) GetQueuedTasks() ([]*Task, error) {
	return tq.TaskQueue.GetAllTasks()
}

// GetJobStatus gets the status of a job by name (TUI-specific convenience method)
func (tq *TaskQueue) GetJobStatus(jobName string) (map[string]string, error) {
	task, err := tq.TaskQueue.GetTaskByName(jobName)
	if err != nil {
		return nil, err
	}
	if task == nil {
		return map[string]string{"status": "not_found"}, nil
	}

	return map[string]string{
		"status":  task.Status,
		"task_id": task.ID,
	}, nil
}

// GetMetrics retrieves metrics for a job (TUI-specific: currently returns empty)
func (tq *TaskQueue) GetMetrics(_ string) (map[string]string, error) {
	// This method doesn't exist in internal queue, return empty for now
	return map[string]string{}, nil
}

// ListDatasets retrieves available datasets from the filesystem
func (tq *TaskQueue) ListDatasets() ([]model.DatasetInfo, error) {
	var datasets []model.DatasetInfo

	// Scan the active data directory for datasets
	dataDir := tq.config.BasePath
	if dataDir == "" {
		return datasets, nil
	}

	entries, err := os.ReadDir(dataDir)
	if err != nil {
		// Directory might not exist yet, return empty
		return datasets, nil
	}

	for _, entry := range entries {
		if entry.IsDir() {
			info, err := entry.Info()
			if err != nil {
				continue
			}
			datasets = append(datasets, model.DatasetInfo{
				Name:       entry.Name(),
				SizeBytes:  info.Size(),
				Location:   filepath.Join(dataDir, entry.Name()),
				LastAccess: time.Now(),
			})
		}
	}

	return datasets, nil
}

// ListExperiments retrieves experiment list
func (tq *TaskQueue) ListExperiments() ([]string, error) {
	return tq.expManager.ListExperiments()
}

// GetExperimentDetails retrieves formatted experiment details
func (tq *TaskQueue) GetExperimentDetails(commitID string) (string, error) {
	meta, err := tq.expManager.ReadMetadata(commitID)
	if err != nil {
		return "", err
	}

	metrics, err := tq.expManager.GetMetrics(commitID)
	if err != nil {
		return "", err
	}

	output := fmt.Sprintf("Experiment: %s\n", meta.JobName)
	output += fmt.Sprintf("Commit ID: %s\n", meta.CommitID)
	output += fmt.Sprintf("User: %s\n", meta.User)
	output += fmt.Sprintf("Timestamp: %d\n\n", meta.Timestamp)
	output += "Metrics:\n"

	if len(metrics) == 0 {
		output += "  No metrics logged.\n"
	} else {
		for _, m := range metrics {
			output += fmt.Sprintf("  %s: %.4f (Step: %d)\n", m.Name, m.Value, m.Step)
		}
	}

	return output, nil
}

// Close closes the task queue
func (tq *TaskQueue) Close() error {
	return tq.TaskQueue.Close()
}

// MLServer is an alias for network.MLServer for backward compatibility
type MLServer = network.MLServer

// NewMLServer creates a new ML server connection
func NewMLServer(cfg *config.Config) (*MLServer, error) {
	// Local mode: skip SSH entirely
	if cfg.Host == "" {
		return network.NewMLServer("", "", "", 0, "")
	}

	return network.NewMLServer(cfg.Host, cfg.User, cfg.SSHKey, cfg.Port, cfg.KnownHosts)
}
