package workbench

import (
	"encoding/json"
	"errors"
	"regexp"
	"strings"
	"time"
)

var (
	ErrNotFound = errors.New("AI asset not found")
	ErrConflict = errors.New("AI asset conflict")
	codeRE      = regexp.MustCompile(`^[a-z][a-z0-9_-]{1,63}$`)
	variableRE  = regexp.MustCompile(`\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}`)
)

type Variable struct {
	Name     string `json:"name"`
	Label    string `json:"label,omitempty"`
	Default  string `json:"default,omitempty"`
	Required bool   `json:"required"`
}

type PromptTemplate struct {
	ID, Name, Description string         `json:"-"`
	CategoryID            *string        `json:"-"`
	CategoryName          string         `json:"-"`
	Tags                  []string       `json:"-"`
	DepartmentIDs         []string       `json:"-"`
	Enabled               bool           `json:"-"`
	CurrentVersion        *int           `json:"-"`
	Current               *PromptVersion `json:"-"`
	Revision              int64          `json:"-"`
	CreatedAt, UpdatedAt  time.Time      `json:"-"`
}

func (p PromptTemplate) MarshalJSON() ([]byte, error) {
	type output struct {
		ID             string         `json:"id"`
		Name           string         `json:"name"`
		Description    string         `json:"description"`
		CategoryID     *string        `json:"category_id,omitempty"`
		CategoryName   string         `json:"category_name"`
		Tags           []string       `json:"tags"`
		DepartmentIDs  []string       `json:"department_ids"`
		Enabled        bool           `json:"enabled"`
		CurrentVersion *int           `json:"current_version,omitempty"`
		Current        *PromptVersion `json:"current,omitempty"`
		Revision       int64          `json:"revision"`
		CreatedAt      time.Time      `json:"created_at"`
		UpdatedAt      time.Time      `json:"updated_at"`
	}
	return json.Marshal(output{p.ID, p.Name, p.Description, p.CategoryID, p.CategoryName, nonNil(p.Tags), nonNil(p.DepartmentIDs), p.Enabled, p.CurrentVersion, p.Current, p.Revision, p.CreatedAt, p.UpdatedAt})
}

type PromptVersion struct {
	ID         string     `json:"id"`
	TemplateID string     `json:"template_id"`
	Version    int        `json:"version"`
	Content    string     `json:"content"`
	Variables  []Variable `json:"variables"`
	ChangeNote string     `json:"change_note"`
	CreatedAt  time.Time  `json:"created_at"`
}

type PromptInput struct {
	Name, Description   string
	CategoryID          *string
	Tags, DepartmentIDs []string
	Enabled             bool
	Content, ChangeNote string
	Variables           []Variable
}

type KnowledgeBase struct {
	ID            string    `json:"id"`
	Name          string    `json:"name"`
	Description   string    `json:"description"`
	RetrievalMode string    `json:"retrieval_mode"`
	ChunkSize     int       `json:"chunk_size"`
	ChunkOverlap  int       `json:"chunk_overlap"`
	DepartmentIDs []string  `json:"department_ids"`
	Enabled       bool      `json:"enabled"`
	Revision      int64     `json:"revision"`
	DocumentCount int       `json:"document_count"`
	ChunkCount    int       `json:"chunk_count"`
	CreatedAt     time.Time `json:"created_at"`
	UpdatedAt     time.Time `json:"updated_at"`
}

type KnowledgeDocument struct {
	ID              string    `json:"id"`
	KnowledgeBaseID string    `json:"knowledge_base_id"`
	Title           string    `json:"title"`
	SourceType      string    `json:"source_type"`
	SourceURI       string    `json:"source_uri"`
	ContentSHA256   string    `json:"content_sha256"`
	Status          string    `json:"status"`
	StatusMessage   string    `json:"status_message"`
	CharCount       int       `json:"char_count"`
	ChunkCount      int       `json:"chunk_count"`
	CreatedAt       time.Time `json:"created_at"`
	UpdatedAt       time.Time `json:"updated_at"`
}

type SearchHit struct {
	ChunkID       string  `json:"chunk_id"`
	DocumentID    string  `json:"document_id"`
	DocumentTitle string  `json:"document_title"`
	Content       string  `json:"content"`
	ChunkIndex    int     `json:"chunk_index"`
	Score         float64 `json:"score"`
}

type Tool struct {
	ID                string          `json:"id"`
	Code              string          `json:"code"`
	Name              string          `json:"name"`
	Description       string          `json:"description"`
	EndpointURL       string          `json:"endpoint_url"`
	HTTPMethod        string          `json:"http_method"`
	InputSchema       json.RawMessage `json:"input_schema"`
	TimeoutSeconds    int             `json:"timeout_seconds"`
	DepartmentIDs     []string        `json:"department_ids"`
	Enabled           bool            `json:"enabled"`
	HasSecretHeaders  bool            `json:"has_secret_headers"`
	Revision          int64           `json:"revision"`
	CreatedAt         time.Time       `json:"created_at"`
	UpdatedAt         time.Time       `json:"updated_at"`
	EncryptedHeaders  []byte          `json:"-"`
	HeadersKEKVersion int             `json:"-"`
}

type ToolInput struct {
	Code, Name, Description, EndpointURL, HTTPMethod string
	Headers                                          map[string]string
	InputSchema                                      json.RawMessage
	TimeoutSeconds                                   int
	DepartmentIDs                                    []string
	Enabled                                          bool
}

type ApplicationConfig struct {
	Model            string   `json:"model"`
	PromptTemplateID string   `json:"prompt_template_id,omitempty"`
	KnowledgeBaseIDs []string `json:"knowledge_base_ids"`
	ToolIDs          []string `json:"tool_ids"`
	RetrievalTopK    int      `json:"retrieval_top_k"`
	Temperature      float64  `json:"temperature"`
	MaxToolRounds    int      `json:"max_tool_rounds"`
}

type Application struct {
	ID               string             `json:"id"`
	Code             string             `json:"code"`
	Name             string             `json:"name"`
	Description      string             `json:"description"`
	Status           string             `json:"status"`
	DepartmentIDs    []string           `json:"department_ids"`
	DraftConfig      ApplicationConfig  `json:"draft_config"`
	PublishedVersion *int               `json:"published_version,omitempty"`
	PublishedConfig  *ApplicationConfig `json:"published_config,omitempty"`
	Revision         int64              `json:"revision"`
	CreatedAt        time.Time          `json:"created_at"`
	UpdatedAt        time.Time          `json:"updated_at"`
}

type ApplicationRun struct {
	ID             string    `json:"id"`
	ApplicationID  string    `json:"application_id"`
	RequestID      string    `json:"request_id"`
	Status         string    `json:"status"`
	Error          string    `json:"error"`
	Version        int       `json:"version"`
	LatencyMS      int64     `json:"latency_ms"`
	RetrievalCount int       `json:"retrieval_count"`
	ToolCount      int       `json:"tool_count"`
	CreatedAt      time.Time `json:"created_at"`
}

type NotificationChannel struct {
	ID                      string    `json:"id"`
	Name                    string    `json:"name"`
	WebhookURL              string    `json:"webhook_url"`
	EventPatterns           []string  `json:"event_patterns"`
	Enabled                 bool      `json:"enabled"`
	HasSigningSecret        bool      `json:"has_signing_secret"`
	Revision                int64     `json:"revision"`
	CreatedAt               time.Time `json:"created_at"`
	UpdatedAt               time.Time `json:"updated_at"`
	EncryptedSigningSecret  []byte    `json:"-"`
	SigningSecretKEKVersion int       `json:"-"`
}

type NotificationDelivery struct {
	ID             string          `json:"id"`
	ChannelID      string          `json:"channel_id"`
	ChannelName    string          `json:"channel_name"`
	EventID        string          `json:"event_id"`
	EventType      string          `json:"event_type"`
	Status         string          `json:"status"`
	LastError      string          `json:"last_error"`
	Payload        json.RawMessage `json:"payload"`
	Attempts       int             `json:"attempts"`
	ResponseStatus *int            `json:"response_status,omitempty"`
	DeliveredAt    *time.Time      `json:"delivered_at,omitempty"`
	CreatedAt      time.Time       `json:"created_at"`
	UpdatedAt      time.Time       `json:"updated_at"`
}

func RenderPrompt(content string, definitions []Variable, provided map[string]any) (string, error) {
	values := make(map[string]string, len(definitions))
	missing := make([]string, 0)
	for _, variable := range definitions {
		if raw, ok := provided[variable.Name]; ok && strings.TrimSpace(toString(raw)) != "" {
			values[variable.Name] = toString(raw)
		} else if variable.Default != "" {
			values[variable.Name] = variable.Default
		} else if variable.Required {
			missing = append(missing, variable.Name)
		} else {
			values[variable.Name] = ""
		}
	}
	if len(missing) > 0 {
		return "", errors.New("缺少必填变量: " + strings.Join(missing, ", "))
	}
	return variableRE.ReplaceAllStringFunc(content, func(match string) string {
		parts := variableRE.FindStringSubmatch(match)
		if value, ok := values[parts[1]]; ok {
			return value
		}
		return match
	}), nil
}

func toString(value any) string {
	if text, ok := value.(string); ok {
		return text
	}
	encoded, _ := json.Marshal(value)
	return string(encoded)
}

func nonNil[T any](value []T) []T {
	if value == nil {
		return []T{}
	}
	return value
}
