package filters

import (
	"encoding/json"
	"fmt"

	"github.com/mudler/LocalAGI/core/state"
	"github.com/mudler/LocalAGI/core/types"
	"github.com/mudler/LocalAGI/pkg/config"
	"github.com/mudler/LocalAGI/pkg/llm"
	"github.com/sashabaranov/go-openai"
	"github.com/sashabaranov/go-openai/jsonschema"
)

const FilterClassifier = "classifier"

type ClassifierFilter struct {
	name         string
	client       *openai.Client
	model        string
	description  string
	allowOnMatch bool
	isTrigger    bool
}

type ClassifierFilterConfig struct {
	Name         string `json:"name"`
	Model        string `json:"model,omitempty"`
	APIURL       string `json:"api_url,omitempty"`
	Description  string `json:"description"`
	AllowOnMatch bool   `json:"allow_on_match"`
	IsTrigger    bool   `json:"is_trigger"`
}

func NewClassifierFilter(configJSON string, a *state.AgentConfig) (*ClassifierFilter, error) {
	var cfg ClassifierFilterConfig
	if err := json.Unmarshal([]byte(configJSON), &cfg); err != nil {
		return nil, err
	}
	var model string
	if cfg.Model != "" {
		model = cfg.Model
	} else {
		model = a.Model
	}
	if cfg.Name == "" {
		return nil, fmt.Errorf("Classifier with no name")
	}
	if cfg.Description == "" {
		return nil, fmt.Errorf("%s classifier has no description", cfg.Name)
	}
	apiUrl := a.APIURL
	if cfg.APIURL != "" {
		apiUrl = cfg.APIURL
	}
	client := llm.NewClient(a.APIKey, apiUrl, "1m")

	return &ClassifierFilter{
		name:         cfg.Name,
		model:        model,
		description:  cfg.Description,
		client:       client,
		allowOnMatch: cfg.AllowOnMatch,
		isTrigger:    cfg.IsTrigger,
	}, nil
}

const fmtT = `
  Does the below message fit the description "%s"

  %s
  `

func (f *ClassifierFilter) Name() string { return f.name }
func (f *ClassifierFilter) Apply(job *types.Job) (bool, error) {
	input := extractInputFromJob(job)
	guidance := fmt.Sprintf(fmtT, f.description, input)
	var result struct {
		Asserted bool `json:"answer"`
	}
	err := llm.GenerateTypedJSONWithGuidance(job.GetContext(), f.client, guidance, f.model, jsonschema.Definition{
		Type: jsonschema.Object,
		Properties: map[string]jsonschema.Definition{
			"answer": {
				Type:        jsonschema.Boolean,
				Description: "The answer to the first question",
			},
		},
		Required: []string{"answer"},
	}, &result)
	if err != nil {
		return false, err
	}

	if result.Asserted {
		return f.allowOnMatch, nil
	}
	return !f.allowOnMatch, nil
}

func (f *ClassifierFilter) IsTrigger() bool {
	return f.isTrigger
}

func ClassifierFilterConfigMeta() config.FieldGroup {
	return config.FieldGroup{
		Name:  FilterClassifier,
		Label: "Classifier Filter/Trigger",
		Fields: []config.Field{
			{Name: "name", Label: "Name", Type: "text", Required: true},
			{Name: "model", Label: "Model", Type: "text", Required: false,
				HelpText: "The LLM to use, usually a smaller one. Leave blank to use the same as the agent's"},
			{Name: "api_url", Label: "API URL", Type: "url", Required: false,
				HelpText: "The URL of the LLM service if different from the agent's"},
			{Name: "description", Label: "Description", Type: "text", Required: true,
				HelpText: "Describe the type of content to match against e.g. 'technical support request'"},
			{Name: "allow_on_match", Label: "Allow on Match", Type: "checkbox", Required: true},
			{Name: "is_trigger", Label: "Is Trigger", Type: "checkbox", Required: true},
		},
	}
}
