package main

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

	"go.temporal.io/sdk/client"
	"github.com/rockliang/poimen/workflows/action/llm"
	"github.com/rockliang/poimen/workflows/internal/config"
	"github.com/rockliang/poimen/workflows/internal/health"
	"github.com/rockliang/poimen/workflows/internal/logging"
	"github.com/rockliang/poimen/workflows/internal/routing"
	"github.com/rockliang/poimen/workflows/statemachine"
)

func main() {
	var (
		// Orchestrator flags
		repoPath         = flag.String("repo", "", "target repo path")
		remoteURL        = flag.String("remote", "", "remote URL")
		milestone        = flag.String("milestone", "T0", "milestone ID")
		dryRun           = flag.Bool("dry-run", false, "disable git push/merge (orchestrator) or skip submit (routing)")
		plannerModel     = flag.String("planner-model", "reasoning", "planner model ID (local-llm)")
		judgeModel       = flag.String("judge-model", "reasoning", "judge model ID (local-llm)")
		implementerModel = flag.String("implementer-model", "ornith:35b", "implementer model ID (local-llm ornith)")
		piProvider       = flag.String("pi-provider", "local-llm", "pi provider name for skills (local-llm)")
		healthCheck      = flag.Bool("health", false, "check health and exit")

		// Routing workflow flags
		routeMsg  = flag.String("route", "", "natural language message for LLM routing")
		specFile  = flag.String("spec", "", "JSON workflow spec file (direct submit, skip LLM)")
		cronSpec  = flag.Bool("cron", false, "treat spec as CronWorkflowSpec")
	)
	flag.Parse()

	// Initialize structured logging
	if err := logging.InitLogger(); err != nil {
		log.Fatalf("failed to initialize logger: %v", err)
	}
	defer logging.Sync()

	// Load configuration first
	cfg, err := config.LoadConfig()
	if err != nil {
		logging.Fatal("failed to load config", logging.Err(err))
	}

	// Connect to Temporal
	logging.Info("connecting to Temporal", logging.String("hostPort", cfg.Temporal.HostPort), logging.String("namespace", cfg.Temporal.Namespace))
	c, err := client.Dial(client.Options{
		HostPort:  cfg.Temporal.HostPort,
		Namespace: cfg.Temporal.Namespace,
	})
	if err != nil {
		logging.Fatal("failed to connect to temporal", logging.Err(err))
	}
	defer c.Close()

	// If health check requested, do it and exit
	if *healthCheck {
		logging.Info("running health check")
		healthChecker := health.NewChecker(c)
		report := healthChecker.Check(context.Background())
		jsonReport, _ := report.ToJSON()
		fmt.Println(string(jsonReport))
		if report.Status != health.StatusHealthy {
			logging.Fatal("health check failed")
		}
		return
	}

	// Handle routing workflow mode
	if *routeMsg != "" || *specFile != "" {
		runRoutingWorkflow(c, *routeMsg, *specFile, *cronSpec, *dryRun)
		return
	}

	// Validate required flags for orchestrator workflow
	if *repoPath == "" || *remoteURL == "" {
		logging.Fatal("--repo and --remote flags are required (or use --route/--spec for routing workflow)")
	}



	// Build OrchestratorInput
	input := statemachine.OrchestratorInput{
		TargetRepoPath:     *repoPath,
		RemoteURL:          *remoteURL,
		Milestone:          *milestone,
		DryRun:             *dryRun,
		MaxCyclesBeforeCAN: 100,
		PiProvider:         *piProvider,
		Config: statemachine.OrchestratorConfig{
			SystemPrompt: "You are an expert software developer orchestrating multi-agent work.",
			Skills:       []statemachine.SkillRef{},
			RolePrompts: map[string]statemachine.PromptSpec{
				"planner": {
					TemplateRef: "planner/default.tmpl",
					Model: statemachine.ModelSpec{
						ModelID:  *plannerModel,
						Thinking: "adaptive",
						Effort:   "high",
					},
				},
				"judge": {
					TemplateRef: "judge/default.tmpl",
					Model: statemachine.ModelSpec{
						ModelID:  *judgeModel,
						Thinking: "adaptive",
						Effort:   "high",
					},
				},
				"implementer": {
					TemplateRef: "implementer/default.tmpl",
					Model: statemachine.ModelSpec{
						ModelID: *implementerModel,
					},
				},
			},
			Tuning: statemachine.NewActivityTuning(),
		},
	}

	// Health check: verify local LLM API is reachable
	logging.Info("checking local LLM API connectivity", logging.String("url", "https://api.riotpiao.com"))
	llmClient, err := llm.NewClient()
	if err != nil {
		logging.Fatal("failed to create LLM client", logging.Err(err))
	}
	if err := llmClient.HealthCheck(context.Background()); err != nil {
		logging.Fatal("local LLM API health check failed", logging.Err(err), logging.String("hint", "ensure homelab-frontend gateway is running and accessible"))
	}
	logging.Info("local LLM API is reachable", logging.String("planner-model", *plannerModel), logging.String("judge-model", *judgeModel), logging.String("implementer-model", *implementerModel))

	// Start workflow
	workflowID := "orch-" + strings.ReplaceAll(*repoPath, "/", "-")
	logging.Info("starting orchestrator workflow", logging.String("workflowID", workflowID), logging.String("repo", *repoPath))
	run, err := c.ExecuteWorkflow(context.Background(), client.StartWorkflowOptions{
		ID:        workflowID,
		TaskQueue: "poimen-taskqueue",
	}, statemachine.OrchestratorWorkflow, input)
	if err != nil {
		logging.Fatal("failed to start workflow", logging.Err(err))
	}

	fmt.Printf("\n=== Workflow Started ===\n")
	fmt.Printf("Workflow ID: %s\n", workflowID)
	fmt.Printf("Task Queue:  poimen-taskqueue\n")
	fmt.Printf("\n=== Model Configuration ===\n")
	fmt.Printf("Planner Model:     %s\n", *plannerModel)
	fmt.Printf("Judge Model:       %s\n", *judgeModel)
	fmt.Printf("Implementer Model: %s\n", *implementerModel)
	fmt.Printf("\n=== Monitoring ===\n")
	fmt.Printf("Web UI: http://%s:8080/namespaces/%s/workflows/%s\n",
		strings.Split(cfg.Temporal.HostPort, ":")[0], cfg.Temporal.Namespace, workflowID)

	// Optionally wait for completion (with timeout)
	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
	defer cancel()

	var result statemachine.OrchestratorOutput
	if err := run.Get(ctx, &result); err != nil {
		fmt.Printf("\nWorkflow initiated (execution in progress).\n")
		fmt.Printf("Check the Web UI for real-time status updates.\n")
	} else {
		fmt.Printf("\nWorkflow completed: %+v\n", result)
	}
}

// runRoutingWorkflow handles --route and --spec flags
func runRoutingWorkflow(c client.Client, routeMsg, specFile string, isCron, dryRun bool) {
	ctx := context.Background()

	var spec *routing.WorkflowSpec
	var cronSpec *routing.CronWorkflowSpec

	if specFile != "" {
		// Load spec from file
		data, err := os.ReadFile(specFile)
		if err != nil {
			logging.Fatal("failed to read spec file", logging.Err(err))
		}

		validator := routing.NewValidator(nil) // nil KB = skip activity validation
		if isCron {
			cronSpec = &routing.CronWorkflowSpec{}
			if err := json.Unmarshal(data, cronSpec); err != nil {
				logging.Fatal("failed to parse cron spec", logging.Err(err))
			}
			// Validate
			result := validator.ValidateCronWorkflowSpec(cronSpec)
			if !result.Valid {
				logging.Fatal("invalid cron spec", logging.String("errors", result.String()))
			}
		} else {
			spec = &routing.WorkflowSpec{}
			if err := json.Unmarshal(data, spec); err != nil {
				logging.Fatal("failed to parse spec", logging.Err(err))
			}
			// Validate
			result := validator.ValidateWorkflowSpec(spec)
			if !result.Valid {
				logging.Fatal("invalid spec", logging.String("errors", result.String()))
			}
		}
	} else {
		// Use LLM router
		logging.Info("routing message via LLM", logging.String("message", routeMsg))

		kb, err := routing.LoadKnowledgeBaseFromDefaultPath()
		if err != nil {
			logging.Fatal("failed to load knowledge base", logging.Err(err))
		}

		router, err := routing.NewLLMRouter(kb)
		if err != nil {
			logging.Fatal("failed to create LLM router", logging.Err(err))
		}

		output, err := router.Route(ctx, routing.LLMRouterInput{Message: routeMsg})
		if err != nil {
			logging.Fatal("LLM routing failed", logging.Err(err))
		}

		if output.IsCron {
			cronSpec = output.CronSpec
			fmt.Printf("\n=== Generated Cron Spec ===\n")
			fmt.Printf("Name: %s\n", cronSpec.Name)
			fmt.Printf("Schedule: %s\n", cronSpec.Schedule)
			fmt.Printf("States: %d\n", len(cronSpec.States))
		} else {
			spec = output.Spec
			fmt.Printf("\n=== Generated Workflow Spec ===\n")
			fmt.Printf("Name: %s\n", spec.Name)
			fmt.Printf("States: %d\n", len(spec.States))
		}
	}

	if dryRun {
		fmt.Printf("\n[dry-run] Spec generated but not submitted\n")
		if spec != nil {
			data, _ := json.MarshalIndent(spec, "", "  ")
			fmt.Printf("%s\n", data)
		} else if cronSpec != nil {
			data, _ := json.MarshalIndent(cronSpec, "", "  ")
			fmt.Printf("%s\n", data)
		}
		return
	}

	// Submit to Temporal
	if cronSpec != nil {
		// For cron, we'd use Temporal's schedule feature
		// For now, just start as regular workflow (cron scheduling TBD)
		spec = &routing.WorkflowSpec{
			Name:   cronSpec.Name,
			Input:  cronSpec.Input,
			States: cronSpec.States,
		}
		logging.Warn("cron scheduling not yet implemented, running as one-shot workflow")
	}

	workflowID := "routing-" + spec.Name + "-" + time.Now().Format("20060102-150405")
	input := statemachine.RoutingWorkflowInput{Spec: spec}

	run, err := c.ExecuteWorkflow(ctx, client.StartWorkflowOptions{
		ID:        workflowID,
		TaskQueue: "poimen-taskqueue",
	}, statemachine.RoutingWorkflow, input)
	if err != nil {
		logging.Fatal("failed to start routing workflow", logging.Err(err))
	}

	fmt.Printf("\n=== Routing Workflow Started ===\n")
	fmt.Printf("Workflow ID: %s\n", workflowID)
	fmt.Printf("Run ID: %s\n", run.GetRunID())

	// Wait briefly for result
	waitCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
	defer cancel()

	var result statemachine.RoutingWorkflowOutput
	if err := run.Get(waitCtx, &result); err != nil {
		fmt.Printf("\nWorkflow running (check Temporal UI for status)\n")
	} else {
		fmt.Printf("\nWorkflow completed: %s\n", result.Status)
		if len(result.StepResults) > 0 {
			for step, res := range result.StepResults {
				fmt.Printf("  %s: %v\n", step, res)
			}
		}
	}
}
