package api

import (
	"strings"

	"github.com/gofiber/fiber/v2"

	"fiko-ai/internal/protocol"
	"fiko-ai/internal/workflow"
)

// Agent Builder (Faz 9) uçları. Akışlar ve yürütmeler KULLANICIYA AİTTİR: her uç
// s.currentUser(c) ile çalışır, başkasının kaydı 404'tür (varlığı bile sızdırılmaz).
//
// Yürütme ARKA PLANDA çalışır: POST /run "running" kaydını hemen döner, ilerleme
// GET /runs/:id ile izlenir. Böylece LLM/RAG çağrıları isteği kilitlemez.

type workflowResponse struct {
	Workflow workflow.Workflow `json:"workflow"`
}

type workflowsResponse struct {
	Items  []workflow.Workflow `json:"items"`
	Count  int                 `json:"count"`
	Limit  int                 `json:"limit"`
	Offset int                 `json:"offset"`
}

type workflowValidateResponse struct {
	// Savable akış kaydedilebilir mi (yapısal hatalar yok mu)?
	Valid bool `json:"valid"`
	// Runnable çalıştırılabilir mi (yapısal olarak geçerli VE yetenekler bağlı mı)?
	Runnable  bool         `json:"runnable"`
	Errors    []fieldError `json:"errors"`    // kaydı engelleyen hatalar (geçerliyse boş dizi)
	RunErrors []fieldError `json:"runErrors"` // çalıştırmayı engelleyen hatalar (boş dizi)
}

// workflowRunRequest yürütme girdisidir. conversationId isteğe bağlıdır:
// memory node'u bu konuşmanın geçmişini okur.
type workflowRunRequest struct {
	Input          string `json:"input"`
	ConversationID string `json:"conversationId"`
}

type executionResponse struct {
	Execution workflow.Execution `json:"execution"`
}

type executionsResponse struct {
	Items  []workflow.Execution `json:"items"`
	Count  int                  `json:"count"`
	Limit  int                  `json:"limit"`
	Offset int                  `json:"offset"`
}

// workflowsAvailable Agent Builder yapılandırılmamışsa tüm /api/workflows uçlarını 503 yapar.
func (s *Server) workflowsAvailable(c *fiber.Ctx) error {
	if s.deps.Workflows == nil {
		return fail(c, fiber.StatusServiceUnavailable, protocol.ErrCodeInternal, "akış sistemi yapılandırılmamış")
	}
	return c.Next()
}

// listWorkflows GET /api/workflows?q=&limit=&offset= — en son güncellenen önce.
func (s *Server) listWorkflows(c *fiber.Ctx) error {
	limit, offset, err := s.parsePaging(c)
	if err != nil {
		return err
	}
	f := workflow.ListFilter{Query: strings.Clone(c.Query("q"))}
	items, err := s.deps.Workflows.List(c.UserContext(), s.currentUser(c), f, limit, offset)
	if err != nil {
		return failWorkflow(c, err)
	}
	return c.JSON(workflowsResponse{Items: items, Count: len(items), Limit: limit, Offset: offset})
}

// getWorkflow GET /api/workflows/:id
func (s *Server) getWorkflow(c *fiber.Ctx) error {
	w, err := s.deps.Workflows.Get(c.UserContext(), s.currentUser(c), strings.Clone(c.Params("id")))
	if err != nil {
		return failWorkflow(c, err)
	}
	return c.JSON(workflowResponse{Workflow: *w})
}

// createWorkflow POST /api/workflows — kimlik, sahip ve zaman damgaları sunucuya aittir.
// TASLAK kaydedilebilir: yalnızca yapısal geçerlilik gerekir (Check değil).
func (s *Server) createWorkflow(c *fiber.Ctx) error {
	in, err := parseWorkflowBody(c)
	if err != nil {
		return err
	}
	return s.writeWorkflow(c, "workflow.create", fiber.StatusCreated, func() (*workflow.Workflow, error) {
		return s.deps.Workflows.Create(c.UserContext(), s.currentUser(c), in)
	})
}

// updateWorkflow PUT /api/workflows/:id — akışı gövdeyle TAMAMEN değiştirir.
func (s *Server) updateWorkflow(c *fiber.Ctx) error {
	in, err := parseWorkflowBody(c)
	if err != nil {
		return err
	}
	userID, id := s.currentUser(c), strings.Clone(c.Params("id"))
	return s.writeWorkflow(c, "workflow.update", fiber.StatusOK, func() (*workflow.Workflow, error) {
		return s.deps.Workflows.Update(c.UserContext(), userID, id, in)
	})
}

// duplicateWorkflow POST /api/workflows/:id/duplicate — "<ad> (kopya)" adıyla yeni akış.
func (s *Server) duplicateWorkflow(c *fiber.Ctx) error {
	userID, id := s.currentUser(c), strings.Clone(c.Params("id"))
	return s.writeWorkflow(c, "workflow.duplicate", fiber.StatusCreated, func() (*workflow.Workflow, error) {
		return s.deps.Workflows.Duplicate(c.UserContext(), userID, id)
	})
}

// deleteWorkflow DELETE /api/workflows/:id — akış ve yürütme geçmişi birlikte silinir.
func (s *Server) deleteWorkflow(c *fiber.Ctx) error {
	userID, id := s.currentUser(c), strings.Clone(c.Params("id"))
	if err := s.deps.Workflows.Delete(c.UserContext(), userID, id); err != nil {
		s.auditFailure(userID, "workflow.delete", id+": "+err.Error())
		return failWorkflow(c, err)
	}
	s.auditSuccess(userID, "workflow.delete", id)
	return c.SendStatus(fiber.StatusNoContent)
}

// validateWorkflow POST /api/workflows/validate[?id=<workflowId>]
// Kaydetmeden denetler. Geçersizlik HATA değil SONUÇTUR: her zaman 200 döner.
// Böylece arayüz canlı doğrulama yapabilir ve alan hatalarını gösterebilir.
func (s *Server) validateWorkflow(c *fiber.Ctx) error {
	in, err := parseWorkflowBody(c)
	if err != nil {
		return err
	}
	got := s.deps.Workflows.Inspect(in)
	return c.JSON(workflowValidateResponse{
		Valid:     len(got.Errors) == 0,
		Runnable:  got.Runnable,
		Errors:    workflowFields(got.Errors),
		RunErrors: workflowFields(got.RunErrors),
	})
}

// workflowMeta GET /api/workflows/meta — palet, operatörler ve sunucu sınırları.
// Node türlerinin hangisinin çalıştırılabilir olduğunu sunucu bildirir; arayüz
// bağlı olmayan türü çalıştırılabilir göstermez.
func (s *Server) workflowMeta(c *fiber.Ctx) error {
	return c.JSON(s.deps.Workflows.Meta())
}

// runWorkflow POST /api/workflows/:id/run — akışı ARKA PLANDA başlatır.
//
// Yanıt 202 + "running" kaydıdır: LLM/RAG çağrıları isteği kilitlemez.
// İlerleme GET /api/workflows/runs/:id ile izlenir; kalıcı hata run kaydında görünür
// (HTTP yanıtı yalnızca BAŞLATMA başarısızlıklarını bildirir).
func (s *Server) runWorkflow(c *fiber.Ctx) error {
	var in workflowRunRequest
	if err := c.BodyParser(&in); err != nil {
		return fail(c, fiber.StatusBadRequest, protocol.ErrCodeBadRequest, "geçersiz istek gövdesi")
	}
	userID, id := s.currentUser(c), strings.Clone(c.Params("id"))
	exec, err := s.deps.Workflows.Start(c.UserContext(), userID, id, workflow.StartInput{
		Input:          strings.Clone(in.Input),
		ConversationID: strings.Clone(in.ConversationID),
	})
	if err != nil {
		s.auditFailure(userID, "workflow.run", id+": "+err.Error())
		return failWorkflow(c, err)
	}
	s.auditSuccess(userID, "workflow.run", "workflow="+id+" execution="+exec.ID)
	return c.Status(fiber.StatusAccepted).JSON(executionResponse{Execution: *exec})
}

// getExecution GET /api/workflows/runs/:id — yürütme kaydı ve adımları (ilerleme buradan izlenir).
func (s *Server) getExecution(c *fiber.Ctx) error {
	e, err := s.deps.Workflows.GetExecution(c.UserContext(), s.currentUser(c), strings.Clone(c.Params("id")))
	if err != nil {
		return failWorkflow(c, err)
	}
	return c.JSON(executionResponse{Execution: *e})
}

// listExecutions GET /api/workflows/runs?workflowId=&status=&limit=&offset= — en yeni önce.
// Akış silinse bile geçmiş okunabilir (kayıtlar akıştan bağımsız tutulur).
func (s *Server) listExecutions(c *fiber.Ctx) error {
	limit, offset, err := s.parsePaging(c)
	if err != nil {
		return err
	}
	status := workflow.RunStatus(strings.ToLower(strings.TrimSpace(c.Query("status"))))
	if status != "" && !status.Valid() {
		return fail(c, fiber.StatusBadRequest, protocol.ErrCodeBadRequest,
			"status running, succeeded, failed veya cancelled olmalı")
	}
	f := workflow.ExecutionFilter{
		WorkflowID: strings.Clone(strings.TrimSpace(c.Query("workflowId"))),
		Status:     status,
	}
	items, err := s.deps.Workflows.ListExecutions(c.UserContext(), s.currentUser(c), f, limit, offset)
	if err != nil {
		return failWorkflow(c, err)
	}
	return c.JSON(executionsResponse{Items: items, Count: len(items), Limit: limit, Offset: offset})
}

// cancelExecution POST /api/workflows/runs/:id/cancel
//
// İptal ASENKRONDUR: dönen kayıt hâlâ "running" olabilir; kesin durum GET /runs/:id ile
// okunur. Yalnızca BAŞLATMA başarısızlıkları HTTP hatasıdır (bitmiş yürütme → 409).
func (s *Server) cancelExecution(c *fiber.Ctx) error {
	userID, id := s.currentUser(c), strings.Clone(c.Params("id"))
	e, err := s.deps.Workflows.Cancel(c.UserContext(), userID, id)
	if err != nil {
		s.auditFailure(userID, "workflow.cancel", id+": "+err.Error())
		return failWorkflow(c, err)
	}
	s.auditSuccess(userID, "workflow.cancel", id)
	return c.JSON(executionResponse{Execution: *e})
}

// writeWorkflow yazma işlemini çalıştırır, denetim kaydı alır ve akışı yanıtlar.
func (s *Server) writeWorkflow(c *fiber.Ctx, action string, status int, op func() (*workflow.Workflow, error)) error {
	userID := s.currentUser(c)
	w, err := op()
	if err != nil {
		s.auditFailure(userID, action, err.Error())
		return failWorkflow(c, err)
	}
	s.auditSuccess(userID, action, "name="+w.Name+" id="+w.ID)
	return c.Status(status).JSON(workflowResponse{Workflow: *w})
}

// parseWorkflowBody gövdeyi Workflow'e çevirir. Hata *fiber.Error'dur
// (handler `return err` ile bırakır; gövdeyi errorHandler yazar).
func parseWorkflowBody(c *fiber.Ctx) (workflow.Workflow, error) {
	var in workflow.Workflow
	if err := c.BodyParser(&in); err != nil {
		return workflow.Workflow{}, fiber.NewError(fiber.StatusBadRequest, "geçersiz istek gövdesi")
	}
	return in, nil
}
