package builtin

import (
	"context"
	"encoding/json"
	"fmt"
	"regexp"
	"strings"
	"unicode/utf8"

	"fiko-ai/internal/tool"
)

// form.go, ajanın kullanıcıya İSTEDİĞİ ZAMAN interaktif form göstermesini sağlar.
//
// Fark nedir? biz.add / biz.delete formu bir İŞLEMİ onaylatmak için üretir. ui.form ise
// yalnızca BİLGİ TOPLAR: araç hiçbir şeyi çalıştırmaz, kullanıcı formu gönderince
// cevaplar sohbete kullanıcı mesajı olarak döner ve model bir sonraki turda onları görür.
//
// Aynı hattı kullanır (tool.confirm olayı + tool-confirm kartı); tek ayrım formun
// `action` alanıdır: "ask". Ajan bu eylemde politikayı (never/risky/...) YOK SAYAR ve
// formu her zaman gösterir; çünkü formun kendisi istenen çıktıdır.

// ConfirmActionAsk bilgi formunun eylem adıdır (ajan katmanındaki askAction ile aynı).
const ConfirmActionAsk = "ask"

const (
	formMaxFields  = 12
	formMaxOptions = 20
	formMaxTitle   = 120
	formMaxMessage = 400
	formMaxLabel   = 80
	formMaxHint    = 120
	formMaxValue   = 500
)

// formKeyRe alan anahtarı: harfle başlar; harf, rakam ve _ içerir.
var formKeyRe = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_]{0,39}$`)

// formTypeAlias küçük modellerin yazabildiği alan türü adlarını kanonik türe çevirir.
// Bilinmeyen tür "text"e düşer: yanlış bir tür adı formu bozmasın.
var formTypeAlias = map[string]string{
	"text": "text", "string": "text", "metin": "text",
	"textarea": "textarea", "multiline": "textarea", "long": "textarea", "uzun": "textarea",
	"number": "number", "int": "number", "integer": "number", "float": "number", "sayi": "number", "sayı": "number",
	"date": "date", "tarih": "date",
	"enum": "enum", "select": "enum", "choice": "enum", "dropdown": "enum", "secim": "enum", "seçim": "enum",
	"bool": "bool", "boolean": "bool", "checkbox": "bool", "yesno": "bool",
}

// formClip metni kırpar ve rune sayısına göre keser.
func formClip(s string, max int) string {
	s = strings.TrimSpace(s)
	if utf8.RuneCountInString(s) <= max {
		return s
	}
	r := []rune(s)
	return strings.TrimSpace(string(r[:max]))
}

func formStr(v any) string {
	switch x := v.(type) {
	case string:
		return x
	case nil:
		return ""
	case float64, int, int64, bool:
		return fmt.Sprint(x)
	}
	return ""
}

// formBool "true", true, "evet", "1" gibi değerleri bool'a çevirir.
func formBool(v any) bool {
	switch x := v.(type) {
	case bool:
		return x
	case string:
		return normalizeBoolValue(x) == "true"
	}
	return false
}

// normalizeBoolValue onay kutusu değerini "true" / "false" metnine indirger.
func normalizeBoolValue(s string) string {
	switch strings.ToLower(strings.TrimSpace(s)) {
	case "true", "evet", "yes", "1", "on", "e", "x":
		return "true"
	}
	return "false"
}

// formStringList seçenek listesini okur (dizi ya da JSON metni); tekilleştirir ve sınırlar.
func formStringList(v any) []string {
	var items []any
	switch x := v.(type) {
	case []any:
		items = x
	case []string:
		for _, s := range x {
			items = append(items, s)
		}
	case string:
		var parsed []any
		if json.Unmarshal([]byte(x), &parsed) == nil {
			items = parsed
		}
	}
	seen := map[string]bool{}
	out := make([]string, 0, len(items))
	for _, it := range items {
		s := formClip(formStr(it), formMaxLabel)
		if s == "" || seen[s] {
			continue
		}
		seen[s] = true
		out = append(out, s)
		if len(out) == formMaxOptions {
			break
		}
	}
	return out
}

// formFieldList `fields` girdisini harita listesine çevirir. Küçük modeller listeyi
// JSON METNİ olarak da gönderebilir; ikisi de kabul edilir.
func formFieldList(v any) []map[string]any {
	switch x := v.(type) {
	case []map[string]any:
		return x
	case []any:
		out := make([]map[string]any, 0, len(x))
		for _, it := range x {
			if m, ok := it.(map[string]any); ok {
				out = append(out, m)
			}
		}
		return out
	case string:
		var parsed []map[string]any
		if json.Unmarshal([]byte(x), &parsed) == nil {
			return parsed
		}
	}
	return nil
}

// buildFormFields ham alanları doğrulanmış ConfirmField'lara çevirir. Sıra KORUNUR
// (sortFields burada kullanılmaz: model alanları anlatım sırasıyla verir).
// Geçersiz anahtar ya da yinelenen anahtar sessizce atlanır.
func buildFormFields(raw []map[string]any) []ConfirmField {
	seen := map[string]bool{}
	out := make([]ConfirmField, 0, len(raw))
	for _, r := range raw {
		key := strings.TrimSpace(formStr(r["key"]))
		if !formKeyRe.MatchString(key) || seen[key] {
			continue
		}
		seen[key] = true

		typ := formTypeAlias[strings.ToLower(strings.TrimSpace(formStr(r["type"])))]
		if typ == "" {
			typ = "text"
		}
		options := formStringList(r["options"])
		if typ == "enum" && len(options) < 2 {
			typ = "text" // tek seçenekli liste anlamsız; serbest metne düş
			options = nil
		}
		if typ != "enum" {
			options = nil
		}

		label := formClip(formStr(r["label"]), formMaxLabel)
		if label == "" {
			label = key
		}
		value := formClip(formStr(r["value"]), formMaxValue)
		switch typ {
		case "bool":
			value = normalizeBoolValue(value)
		case "enum":
			found := false
			for _, o := range options {
				if o == value {
					found = true
				}
			}
			if !found {
				value = ""
			}
		}

		f := ConfirmField{
			Key: key, Label: label, Value: value, Type: typ,
			Editable: true, Required: formBool(r["required"]),
			Hint: formClip(formStr(r["hint"]), formMaxHint),
		}
		for _, o := range options {
			f.Options = append(f.Options, o)
		}
		// Zorunlu ve boş alan "eksik" sayılır (onay kutusu hariç: onun kuralı arayüzde,
		// zorunlu kutu işaretlenmeden gönderilemez).
		f.Missing = f.Required && typ != "bool" && strings.TrimSpace(f.Value) == ""
		out = append(out, f)
		if len(out) == formMaxFields {
			break
		}
	}
	return out
}

// uiFormHandler formu üretir. Hiçbir yan etkisi yoktur; ajan formu kullanıcıya iletir.
func uiFormHandler(_ context.Context, t *tool.Tool, in map[string]any) (map[string]any, error) {
	fields := buildFormFields(formFieldList(in["fields"]))
	if len(fields) == 0 {
		return nil, Fail(t.Name, "en az bir geçerli alan gerekli: fields=[{key, label, type}] (key harfle başlamalı, örn. phone)")
	}
	title := formClip(formStr(in["title"]), formMaxTitle)
	if title == "" {
		title = "Bilgi gerekiyor"
	}
	submit := formClip(formStr(in["submitLabel"]), 30)
	if submit == "" {
		submit = "Gönder"
	}
	cancel := formClip(formStr(in["cancelLabel"]), 30)
	if cancel == "" {
		cancel = "Vazgeç"
	}
	var missing []string
	for _, f := range fields {
		if f.Missing {
			missing = append(missing, f.Key)
		}
	}
	out := confirmResult(ConfirmForm{
		Action:        ConfirmActionAsk,
		Tool:          t.Name,
		Title:         title,
		Message:       formClip(formStr(in["message"]), formMaxMessage),
		Fields:        fields,
		MissingFields: missing,
		ConfirmLabel:  submit,
		CancelLabel:   cancel,
		Destructive:   false,
		Payload:       map[string]any{},
		Risky:         false,
	})
	out["note"] = "FORM KULLANICIYA GÖSTERİLDİ; cevaplar henüz yok. Kullanıcı gönderince cevaplar 'Form cevabı' " +
		"diye başlayan yeni bir mesajla gelir. Aynı formu tekrar açma, soruları yazıyla tekrarlama."
	return out, nil
}

// formDefinitions ui.form aracını tanımlar. Yan etkisizdir (yalnızca form üretir): düşük
// risk, yalnızca execute izni; bu yüzden varsayılan izinle modele sunulur.
func formDefinitions(_ Options) []Def {
	return []Def{{
		Enabled: true,
		Tool: tool.Tool{
			Name:  "ui.form",
			Title: "Kullanıcıya form göster",
			Description: "Kullanıcıya sohbette İNTERAKTİF FORM gösterir (metin, sayı, tarih, seçim listesi, onay kutusu). " +
				"Birden çok bilgi gerekiyorsa, seçenekler sabitse ya da evet/hayır soruyorsan soruyu yazıyla sormak " +
				"yerine bunu çağır. Alanları fields ile ver: key (harfle başlayan, örn. phone), label (kullanıcıya " +
				"görünen ad), type (text, textarea, number, date, enum, bool), enum için options, required, value " +
				"(önceden doldurulmuş), hint. Çağırdıktan sonra tek kısa cümle yaz (örn. 'Formu doldurup gönder.') " +
				"ve DUR: kullanıcı gönderince cevaplar 'Form cevabı' diye başlayan yeni bir mesajla gelir. Formu " +
				"tekrar açma, soruları yazıyla tekrarlama. Kayıt eklemek/silmek için bunu KULLANMA: biz.add ve " +
				"biz.delete kendi onay formunu zaten açar.",
			Tags:        []string{"ui", "form"},
			Risk:        tool.RiskLow,
			Permissions: []string{"execute"},
			InputSchema: obj("type", "object",
				"properties", obj(
					"title", str("Formun başlığı (kısa, örn. 'Yeni müşteri bilgileri')"),
					"message", str("Başlığın altında görünen kısa açıklama (isteğe bağlı)"),
					"fields", obj("type", "array", "description", "Form alanları, görünecekleri sırayla (en çok 12)",
						"items", obj("type", "object",
							"properties", obj(
								"key", str("Alan anahtarı: harfle başlar, harf/rakam/_ (örn. phone)"),
								"label", str("Kullanıcıya görünen ad"),
								"type", str("text | textarea | number | date | enum | bool (varsayılan text)"),
								"options", obj("type", "array", "description", "type=enum için en az 2 seçenek",
									"items", obj("type", "string")),
								"required", obj("type", "boolean", "description", "doldurulmadan gönderilemez"),
								"value", str("Önceden doldurulacak değer (isteğe bağlı)"),
								"hint", str("Alanın altında gri ipucu (isteğe bağlı)"),
							),
							"required", []any{"key"},
						)),
					"submitLabel", str("Gönder düğmesinin yazısı (varsayılan: Gönder)"),
					"cancelLabel", str("Vazgeç düğmesinin yazısı (varsayılan: Vazgeç)"),
				),
				"required", []any{"title", "fields"},
			),
			OutputSchema: obj("type", "object"),
			UI:           tool.UIForm,
			TimeoutMs:    5_000,
		},
		Handler: uiFormHandler,
	}}
}
