package pdks

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"fiko-ai/internal/tool"
)

// tools.go: modele acilan PDKS araclari.
//
// ONAY POLITIKASI (KRITIK): bu dosyadaki YAZMA araclari mevcut onay altyapisina
// baglidir. Risk seviyeleri su sekildedir:
//
//	RiskLow      → pdks.dept.list, pdks.employee.list, pdks.shift.list,
//	               pdks.report.daily, pdks.report.range, pdks.day
//	RiskMedium   → pdks.employee.create, pdks.employee.update, pdks.shift.create,
//	               pdks.shift.update, pdks.dept.create, pdks.dept.update,
//	               pdks.leave.create, pdks.leave.update, pdks.attendance.create
//	RiskHigh     → pdks.dept.delete, pdks.employee.delete, pdks.shift.delete,
//	               pdks.leave.delete, pdks.attendance.delete
//
// Kullanici Ayarlar → "Yazma / silme onayi" ile bu riskler uzerinden onay
// dialogunu yonetir (confirmMode: always | risky | missing | never).
// `confirmed` alani model semasindan GIZLIDIR; yalnizca onay ekranindan gelir.
//
// NEDEN SILMELER HIGH? Bir gecmis kaydi silmek denetim izini bozar: "Ali 8 saat
// calisti" kaydi gittiginde kimse dogrulayamaz. Bu yuzden varsayilan confirmMode
// (risky) bunlari otomatik dialoga dusurur.

// ToolDef, kaydedilecek tool tanimidir (memory.Definitions ile ayni sekil).
type ToolDef struct {
	Tool    tool.Tool
	Handler tool.Handler
}

// ToolSet, PDKS servisini modele acar.
type ToolSet struct {
	svc *Service
}

// NewToolSet, arac setini olusturur. svc nil ise arac tanimlari bos doner
// (PDKS yapilandirilmamis kurulumda model bu araclari GORMER).
func NewToolSet(svc *Service) *ToolSet { return &ToolSet{svc: svc} }

// Definitions, PDKS tool tanimlarini dondurur.
func (t *ToolSet) Definitions() []ToolDef {
	if t == nil || t.svc == nil {
		return nil
	}
	return []ToolDef{
		{ // ---- OKUMA ----
			Tool: tool.Tool{
				Name:        "pdks.dept.list",
				Title:       "Bölümleri listele",
				Description: "Şirket bölüm ağacını (kök ve alt bölümler) listeler. Personel veya vardiya atamak için önce bölümleri gör.",
				Version:     "1.0.0",
				InputSchema: obj("type", "object", "properties", obj()),
				OutputSchema: obj("type", "object", "properties", obj(
					"items", arr("Bölüm listesi"),
				)),
				Permissions: []string{"execute"},
				Risk:        tool.RiskLow,
				TimeoutMs:   10_000,
				Tags:        []string{"pdks", "personel"},
			},
			Handler: t.listDepartments,
		},
		{ // ---- OKUMA ----
			Tool: tool.Tool{
				Name:        "pdks.employee.list",
				Title:       "Personeli listele",
				Description: "PDKS'e kayıtlı personel listesini verir. Giriş/çıkış kaydı tutmadan veya rapor almadan ÖNCE personelin var olduğundan emin ol; yoksa önce pdks.employee.create ile ekle.",
				Version:     "1.0.0",
				InputSchema: obj("type", "object", "properties", obj()),
				OutputSchema: obj("type", "object", "properties", obj(
					"items", arr("Personel listesi (id, ad, bölüm)"),
				)),
				Permissions: []string{"execute"},
				Risk:        tool.RiskLow,
				TimeoutMs:   10_000,
				Tags:        []string{"pdks", "personel"},
			},
			Handler: t.listEmployees,
		},
		{ // ---- OKUMA ----
			Tool: tool.Tool{
				Name:  "pdks.attendance.create",
				Title: "Giriş / çıkış / mola kaydı tut",
				Description: "Personel için giriş, çıkış veya mola kaydı oluşturur. type: 'in' (giriş), 'out' (çıkış), 'break_start' (mola başlangıcı), 'break_end' (mola bitişi). " +
					"KURALLAR: bir giriş yapılmadan çıkış/mola yazılamaz; açık giriş varken ikinci giriş reddedilir. " +
					"at verilmezse şu an kullanılır. Yanlış kayıt düzeltmesi için önce kaydı sil (pdks.attendance.delete), sonra doğrusunu ekle.",
				Version: "1.0.0",
				InputSchema: obj(
					"type", "object",
					"properties", obj(
						"empId", obj("type", "string", "description", "Personel kimliği (pdks.employee.list'ten alınır)",
							"minLength", 3, "maxLength", 64),
						"attendanceType", obj("type", "string", "description", "in | out | break_start | break_end",
							"enum", []any{"in", "out", "break_start", "break_end"}),
						"at", obj("type", "string", "description",
							"Zaman damgası (ISO). Geçmişe kayıt/düzeltme için; boş bırakılırsa şu an alınır.",
							"maxLength", 40),
						"note", str("Serbest not (örn. 'doktor randevusu')"),
					),
					"required", []any{"empId", "attendanceType"},
				),
				OutputSchema: obj("type", "object", "properties", obj(
					"id", str("Oluşturulan kaydın kimliği"),
					"at", str("Kayıt zamanı"),
					"dayOf", str("Kaydın ait olduğu gün (YYYY-MM-DD)"),
				)),
				Permissions: []string{"execute"},
				// MEDIUM: geri alınabilir ama denetime tabi devam kaydı.
				Risk:      tool.RiskMedium,
				TimeoutMs: 10_000,
				Tags:      []string{"pdks", "devam"},
			},
			Handler: t.createAttendance,
		},
		{ // ---- OKUMA ----
			Tool: tool.Tool{
				Name:  "pdks.report.daily",
				Title: "Günlük devam raporu",
				Description: "Bir günün TÜM personel devam durumunu verir: giriş/çıkış saati, çalışma ve mola süresi, geç kalma, erken çıkış, mesai, durum (present/absent/leave/open). " +
					"day verilmezse bugün. Tarihi YYYY-MM-DD biçiminde yaz.",
				Version: "1.0.0",
				InputSchema: obj(
					"type", "object",
					"properties", obj(
						"day", obj("type", "string", "description", "Gün (YYYY-MM-DD). Boş = bugün",
							"maxLength", 10),
					),
				),
				OutputSchema: obj("type", "object", "properties", obj(
					"items", arr("Personel bazında günlük özet"),
				)),
				Permissions: []string{"execute"},
				Risk:        tool.RiskLow,
				TimeoutMs:   15_000,
				Tags:        []string{"pdks", "rapor"},
			},
			Handler: t.dailyReport,
		},
		{ // ---- OKUMA ----
			Tool: tool.Tool{
				Name:  "pdks.report.range",
				Title: "Dönem devam raporu",
				Description: "Tarih aralığındaki toplam çalışma/mola/mesai sürelerini ve personel kırılımını verir (haftalık/aylık devam raporu). " +
					"from ve to ZORUNLUDUR (YYYY-MM-DD). Aralık en fazla 366 gün olabilir.",
				Version: "1.0.0",
				InputSchema: obj(
					"type", "object",
					"properties", obj(
						"empId", obj("type", "string", "description",
							"Tek personel için rapor; boş = tüm personel", "maxLength", 64),
						"from", obj("type", "string", "description", "Başlangıç (YYYY-MM-DD)",
							"minLength", 10, "maxLength", 10),
						"to", obj("type", "string", "description", "Bitiş (YYYY-MM-DD)",
							"minLength", 10, "maxLength", 10),
					),
					"required", []any{"from", "to"},
				),
				OutputSchema: obj("type", "object", "properties", obj(
					"workMin", num("Toplam çalışma (dk)"),
					"breakMin", num("Toplam mola (dk)"),
					"overtimeMin", num("Toplam mesai (dk)"),
					"present", num("Gelen gün sayısı"),
					"absent", num("Gelmediği gün"),
					"leave", num("İzinli gün"),
					"rows", arr("Personel bazında kırılım"),
				)),
				Permissions: []string{"execute"},
				Risk:        tool.RiskLow,
				TimeoutMs:   30_000,
				Tags:        []string{"pdks", "rapor"},
			},
			Handler: t.rangeReport,
		},
		{ // ---- YAZMA: DÜŞÜK RİSK ----
			Tool: tool.Tool{
				Name:  "pdks.employee.create",
				Title: "Personel ekle",
				Description: "PDKS'e yeni personel kaydı ekler. Personel yoksa giriş/çıkış kaydı tutulamaz; önce buradan ekle. " +
					"departmentId için önce pdks.dept.list çağır.",
				Version: "1.0.0",
				InputSchema: obj(
					"type", "object",
					"properties", obj(
						"name", obj("type", "string", "description", "Personelin adı soyadı",
							"minLength", 2, "maxLength", 80),
						"departmentId", str("Bölüm kimliği (pdks.dept.list'ten)"),
						"title", str("Görev/unvan"),
						"email", str("E-posta"),
					),
					"required", []any{"name"},
				),
				OutputSchema: obj("type", "object", "properties", obj(
					"id", str("Oluşturulan personelin kimliği"),
					"name", str("Personel adı"),
				)),
				Permissions: []string{"execute"},
				Risk:        tool.RiskMedium,
				TimeoutMs:   10_000,
				Tags:        []string{"pdks", "personel"},
			},
			Handler: t.createEmployee,
		},
		{ // ---- YAZMA: YÜKSEK RİSK (SİLME) ----
			Tool: tool.Tool{
				Name:  "pdks.employee.delete",
				Title: "Personel sil",
				Description: "Personel kaydını SİLER. DİKKAT: geri alınamaz ve geçmiş devam kayıtları da anlamsızlaşır. " +
					"Personel ayrıldıysa silmek yerine pasifleştirme tercih edilmelidir (bu araçta yoksa kullanıcıya bildir).",
				Version: "1.0.0",
				InputSchema: obj(
					"type", "object",
					"properties", obj(
						"empId", obj("type", "string", "description", "Silinecek personelin kimliği",
							"minLength", 3, "maxLength", 64),
					),
					"required", []any{"empId"},
				),
				OutputSchema: obj("type", "object", "properties", obj(
					"status", str("deleted"),
				)),
				Permissions: []string{"execute"},
				Risk:        tool.RiskHigh,
				TimeoutMs:   10_000,
				Tags:        []string{"pdks", "personel"},
			},
			Handler: t.deleteEmployee,
		},
	}
}

// ---- sema yardımcıları (memory/tools.go ile aynı biçim) ----

func obj(pairs ...any) map[string]any {
	m := make(map[string]any, len(pairs)/2)
	for i := 0; i+1 < len(pairs); i += 2 {
		k, _ := pairs[i].(string)
		m[k] = pairs[i+1]
	}
	return m
}

func str(desc string) map[string]any { return obj("type", "string", "description", desc) }

func num(desc string) map[string]any { return obj("type", "number", "description", desc) }

func arr(desc string) map[string]any { return obj("type", "array", "description", desc) }

// ---- girdi çözümleme ----

// punchInput, pdks.attendance.create girdisidir.
type punchInput struct {
	EmpID          string `json:"empId"`
	AttendanceType string `json:"attendanceType"`
	At             string `json:"at"`
	Note           string `json:"note"`
}

// employeeInput, pdks.employee.create girdisidir.
type employeeInput struct {
	Name         string `json:"name"`
	DepartmentID string `json:"departmentId"`
	Title        string `json:"title"`
	Email        string `json:"email"`
}

// dayInput, günlük rapor girdisidir.
type dayInput struct {
	Day string `json:"day"`
}

// rangeInput, dönem raporu girdisidir.
type rangeInput struct {
	EmpID string `json:"empId"`
	From  string `json:"from"`
	To    string `json:"to"`
}

// empIDInput, silme araçlarının ortak girdisidir.
type empIDInput struct {
	EmpID string `json:"empId"`
}

// ---- işleyiciler ----

func (t *ToolSet) listDepartments(ctx context.Context, _ *tool.Tool, _ map[string]any) (map[string]any, error) {
	items, err := t.svc.ListDepartments(ctx)
	if err != nil {
		return nil, err
	}
	return map[string]any{"items": items}, nil
}

func (t *ToolSet) listEmployees(ctx context.Context, _ *tool.Tool, _ map[string]any) (map[string]any, error) {
	items, err := t.svc.ListEmployees(ctx)
	if err != nil {
		return nil, err
	}
	return map[string]any{"items": items}, nil
}

// createAttendance giriş/çıkış/mola kaydı oluşturur.
//
// Hata MODEL'E döner ve açıktır ("zaten giriş yapmış, önce çıkış yapın" gibi).
// Bu, modelin durumu anlamasını sağlar; sessizce yazmaktan uzaktır.
func (t *ToolSet) createAttendance(ctx context.Context, _ *tool.Tool, args map[string]any) (map[string]any, error) {
	var in punchInput
	if err := decodeArgs(args, &in); err != nil {
		return nil, err
	}
	p := Punch{
		EmpID:  strings.TrimSpace(in.EmpID),
		Type:   AttendanceType(strings.TrimSpace(in.AttendanceType)),
		Note:   in.Note,
		Source: SourceManual,
	}
	if strings.TrimSpace(in.At) != "" {
		at, err := parseStamp(strings.TrimSpace(in.At))
		if err != nil {
			return nil, fmt.Errorf("at bicimi gecersiz; ISO biciminde yaz: 2026-10-05T09:00:00")
		}
		p.At = at
	}
	a, err := t.svc.Punch(ctx, p)
	if err != nil {
		return nil, err
	}
	return map[string]any{"id": a.ID, "at": a.At.Format(time.RFC3339), "dayOf": a.DayOf}, nil
}

func (t *ToolSet) dailyReport(ctx context.Context, _ *tool.Tool, args map[string]any) (map[string]any, error) {
	var in dayInput
	_ = decodeArgs(args, &in)
	items, err := t.svc.DailyRange(ctx, strings.TrimSpace(in.Day))
	if err != nil {
		return nil, err
	}
	return map[string]any{"items": items}, nil
}

func (t *ToolSet) rangeReport(ctx context.Context, _ *tool.Tool, args map[string]any) (map[string]any, error) {
	var in rangeInput
	if err := decodeArgs(args, &in); err != nil {
		return nil, err
	}
	rep, err := t.svc.Report(ctx, strings.TrimSpace(in.EmpID), strings.TrimSpace(in.From), strings.TrimSpace(in.To))
	if err != nil {
		return nil, err
	}
	// Rapor struct'ı JSON etiketleriyle döner; model alan adlarını görür.
	return toMap(rep), nil
}

// toMap, JSON etiketli bir değeri map'e çevirir (tool.Handler imzası map ister).
func toMap(v any) map[string]any {
	raw, err := json.Marshal(v)
	if err != nil {
		return map[string]any{}
	}
	out := map[string]any{}
	if err := json.Unmarshal(raw, &out); err != nil {
		return map[string]any{}
	}
	return out
}

func (t *ToolSet) createEmployee(ctx context.Context, _ *tool.Tool, args map[string]any) (map[string]any, error) {
	var in employeeInput
	if err := decodeArgs(args, &in); err != nil {
		return nil, err
	}
	e, err := t.svc.CreateEmployee(ctx, Employee{
		Name:         in.Name,
		DepartmentID: in.DepartmentID,
		Title:        in.Title,
		Email:        in.Email,
		Active:       true,
	})
	if err != nil {
		return nil, err
	}
	return map[string]any{"id": e.ID, "name": e.Name}, nil
}

func (t *ToolSet) deleteEmployee(ctx context.Context, _ *tool.Tool, args map[string]any) (map[string]any, error) {
	var in empIDInput
	if err := decodeArgs(args, &in); err != nil {
		return nil, err
	}
	if err := t.svc.DeleteEmployee(ctx, strings.TrimSpace(in.EmpID)); err != nil {
		return nil, err
	}
	return map[string]any{"status": "deleted"}, nil
}

// decodeArgs, araç girdisini yapılandırılmış tipe çevirir.
func decodeArgs(args map[string]any, out any) error {
	if args == nil {
		return nil
	}
	raw, err := json.Marshal(args)
	if err != nil {
		return fmt.Errorf("girdi okunamadi: %w", err)
	}
	if err := json.Unmarshal(raw, out); err != nil {
		return fmt.Errorf("girdi bicimi gecersiz: %w", err)
	}
	return nil
}

// parseStamp, ISO zaman damgasını çözer (zaman dilimi yoksa yerel saat varsayılır).
func parseStamp(v string) (time.Time, error) {
	s := strings.TrimSpace(v)
	if t, err := time.Parse(time.RFC3339, s); err == nil {
		return t, nil
	}
	if t, err := time.ParseInLocation("2006-01-02T15:04:05", s, time.Local); err == nil {
		return t, nil
	}
	return time.Time{}, fmt.Errorf("zaman damgasi gecersiz")
}
