package agentic

import (
	"context"
	"os"
	"os/exec"
	"strings"

	"github.com/host-uk/core/pkg/framework"
	"github.com/host-uk/core/pkg/log"
)

// Tasks for AI service

// TaskCommit requests Claude to create a commit.
type TaskCommit struct {
	Path    string
	Name    string
	CanEdit bool // allow Write/Edit tools
}

// TaskPrompt sends a custom prompt to Claude.
type TaskPrompt struct {
	Prompt       string
	WorkDir      string
	AllowedTools []string

	taskID string
}

func (t *TaskPrompt) SetTaskID(id string) { t.taskID = id }
func (t *TaskPrompt) GetTaskID() string   { return t.taskID }

// ServiceOptions for configuring the AI service.
type ServiceOptions struct {
	DefaultTools []string
	AllowEdit    bool // global permission for Write/Edit tools
}

// DefaultServiceOptions returns sensible defaults.
func DefaultServiceOptions() ServiceOptions {
	return ServiceOptions{
		DefaultTools: []string{"Bash", "Read", "Glob", "Grep"},
		AllowEdit:    false,
	}
}

// Service provides AI/Claude operations as a Core service.
type Service struct {
	*framework.ServiceRuntime[ServiceOptions]
}

// NewService creates an AI service factory.
func NewService(opts ServiceOptions) func(*framework.Core) (any, error) {
	return func(c *framework.Core) (any, error) {
		return &Service{
			ServiceRuntime: framework.NewServiceRuntime(c, opts),
		}, nil
	}
}

// OnStartup registers task handlers.
func (s *Service) OnStartup(ctx context.Context) error {
	s.Core().RegisterTask(s.handleTask)
	return nil
}

func (s *Service) handleTask(c *framework.Core, t framework.Task) (any, bool, error) {
	switch m := t.(type) {
	case TaskCommit:
		err := s.doCommit(m)
		if err != nil {
			log.Error("agentic: commit task failed", "err", err, "path", m.Path)
		}
		return nil, true, err

	case TaskPrompt:
		err := s.doPrompt(m)
		if err != nil {
			log.Error("agentic: prompt task failed", "err", err)
		}
		return nil, true, err
	}
	return nil, false, nil
}

func (s *Service) doCommit(task TaskCommit) error {
	prompt := Prompt("commit")

	tools := []string{"Bash", "Read", "Glob", "Grep"}
	if task.CanEdit {
		tools = []string{"Bash", "Read", "Write", "Edit", "Glob", "Grep"}
	}

	cmd := exec.CommandContext(context.Background(), "claude", "-p", prompt, "--allowedTools", strings.Join(tools, ","))
	cmd.Dir = task.Path
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin

	return cmd.Run()
}

func (s *Service) doPrompt(task TaskPrompt) error {
	if task.taskID != "" {
		s.Core().Progress(task.taskID, 0.1, "Starting Claude...", &task)
	}

	opts := s.Opts()
	tools := opts.DefaultTools
	if len(tools) == 0 {
		tools = []string{"Bash", "Read", "Glob", "Grep"}
	}

	if len(task.AllowedTools) > 0 {
		tools = task.AllowedTools
	}

	cmd := exec.CommandContext(context.Background(), "claude", "-p", task.Prompt, "--allowedTools", strings.Join(tools, ","))
	if task.WorkDir != "" {
		cmd.Dir = task.WorkDir
	}
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin

	if task.taskID != "" {
		s.Core().Progress(task.taskID, 0.5, "Running Claude prompt...", &task)
	}

	err := cmd.Run()

	if task.taskID != "" {
		if err != nil {
			s.Core().Progress(task.taskID, 1.0, "Failed: "+err.Error(), &task)
		} else {
			s.Core().Progress(task.taskID, 1.0, "Completed", &task)
		}
	}

	return err
}
