package main

import (
	"context"
	"database/sql"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

type Config struct {
	Port          int
	DBPath        string
	RepoRoot      string
	WorktreesRoot string
}

type App struct {
	cfg Config
	db  *sql.DB
	sse *sseHub
}

func main() {
	var (
		port      = flag.Int("port", 8486, "HTTP port")
		dbPath    = flag.String("db", "agent_runs.db", "SQLite database path")
		repoRoot  = flag.String("repo-root", "", "Git repo root (defaults to $PWD)")
		wtRoot    = flag.String("worktrees-root", "/tmp", "Parent dir for worktrees")
	)
	flag.Parse()

	root := *repoRoot
	if root == "" {
		root, _ = os.Getwd()
	}

	db, err := openDB(*dbPath)
	if err != nil {
		log.Fatalf("openDB: %v", err)
	}
	defer db.Close()

	app := &App{
		cfg: Config{
			Port:          *port,
			DBPath:        *dbPath,
			RepoRoot:      root,
			WorktreesRoot: *wtRoot,
		},
		db:  db,
		sse: newSSEHub(),
	}

	mux := http.NewServeMux()
	mux.HandleFunc("/api/health", app.handleHealth)
	mux.HandleFunc("/api/runs", func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case http.MethodPost:
			app.handleCreateRun(w, r)
		case http.MethodGet:
			app.handleListRuns(w, r)
		default:
			http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		}
	})
	mux.HandleFunc("/api/runs/", func(w http.ResponseWriter, r *http.Request) {
		app.routeRun(w, r)
	})

	srv := &http.Server{
		Addr:              fmt.Sprintf(":%d", *port),
		Handler:           mux,
		ReadHeaderTimeout: 5 * time.Second,
	}

	// Graceful shutdown
	go func() {
		sigs := make(chan os.Signal, 1)
		signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
		<-sigs
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		_ = srv.Shutdown(ctx)
	}()

	log.Printf("agent_runner_api listening :%d db=%s repo=%s", *port, *dbPath, root)
	if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
		log.Fatalf("listen: %v", err)
	}
}

// routeRun parses /api/runs/:id[/...] subroutes.
func (a *App) routeRun(w http.ResponseWriter, r *http.Request) {
	path := strings.TrimPrefix(r.URL.Path, "/api/runs/")
	parts := strings.Split(path, "/")
	if len(parts) == 0 || parts[0] == "" {
		http.NotFound(w, r)
		return
	}
	runID := parts[0]

	if len(parts) == 1 {
		if r.Method != http.MethodGet {
			http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
			return
		}
		a.handleGetRun(w, r, runID)
		return
	}

	switch parts[1] {
	case "sse":
		a.handleRunSSE(w, r, runID)
	case "merge":
		if r.Method != http.MethodPost {
			http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
			return
		}
		a.handleMergeRun(w, r, runID)
	case "abort":
		if r.Method != http.MethodPost {
			http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
			return
		}
		a.handleAbortRun(w, r, runID)
	case "evidence":
		// /api/runs/:id/evidence (POST) — attach
		// /api/runs/:id/evidence/:eid/validate (POST) — validate
		if len(parts) == 2 {
			if r.Method != http.MethodPost {
				http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
				return
			}
			a.handleAttachEvidence(w, r, runID)
			return
		}
		if len(parts) == 4 && parts[3] == "validate" {
			if r.Method != http.MethodPost {
				http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
				return
			}
			a.handleValidateEvidence(w, r, runID, parts[2])
			return
		}
		http.NotFound(w, r)
	default:
		http.NotFound(w, r)
	}
}
