package api

import (
	"errors"
	"strings"

	"github.com/gofiber/fiber/v2"

	"fiko-ai/internal/protocol"
	"fiko-ai/internal/tool"
	"fiko-ai/internal/user"
)

// permissionsResponse, kullanıcının tool yetkilerini arayüze taşır.
// Spec: "Permission ayarlamak" kullanıcı arayüzünden yapılabilmeli; varsayılanlar
// güvenli (yalnızca execute), açma işlemi YÖNETİCİYE aittir (üyelik açıkken).
type permissionsResponse struct {
	User   string          `json:"user"`
	Scope  string          `json:"scope"` // "user" (bu kullanıcı) | "default"
	Policy tool.PolicyView `json:"policy"`
	Known  []string        `json:"known"` // seçilebilecek izin adları
	// Users özel politika verilmiş kimlikler. Üye kimliklerini yalnızca yönetici görür.
	Users []string `json:"users"`
	// Admin bu istek sahibinin yetki ayarlayabildiğini söyler: üyelik kapalıyken herkes,
	// açıkken yalnızca yönetici. Arayüz düzenleme düğmelerini buna göre açar/kapatır.
	Admin bool `json:"admin"`
}

// policyRequest gelen izin politikasıdır (izinler AD olarak gelir: ["read","write"]).
type policyRequest struct {
	Grants        []string `json:"grants"`
	AllowHighRisk bool     `json:"allowHighRisk"`
	AllowCritical bool     `json:"allowCritical"`
}

// knownPermissions seçilebilecek izin adlarıdır (UI buradan beslenir).
var knownPermissions = []string{"read", "write", "execute", "delete", "system"}

// permissionsFor, userID için izin özetini üretir. Üye kimlik listesi yönetici değilse boştur:
// bir üye diğer üyelerin kimliğini bu uçtan öğrenmemeli.
func (s *Server) permissionsFor(c *fiber.Ctx, userID string) permissionsResponse {
	store := s.deps.Grants
	admin := s.isAdmin(c)
	users := []string{}
	if admin {
		users = store.Users()
	}
	return permissionsResponse{
		User:   userID,
		Scope:  policyScope(store, userID),
		Policy: store.PolicyFor(userID).View(),
		Known:  knownPermissions,
		Users:  users,
		Admin:  admin,
	}
}

// getPermissions GET /api/permissions — bu kullanıcının tool yetkileri.
func (s *Server) getPermissions(c *fiber.Ctx) error {
	if s.deps.Grants == nil {
		return fail(c, fiber.StatusServiceUnavailable, protocol.ErrCodeInternal, "izin sistemi yapılandırılmamış")
	}
	return c.JSON(s.permissionsFor(c, s.currentUser(c)))
}

// putPermissions PUT /api/permissions — bu kullanıcının tool yetkilerini ayarlar.
// Yetki VERMEK de bir yetki işlemidir: denetim kaydına yazılır.
//
// Üyelik AÇIKKEN yalnızca yönetici çağırabilir. Aksi hâlde herhangi bir üye kendine
// delete/system verip izin sistemini anlamsız kılardı. Üye için uç görünmez (404).
func (s *Server) putPermissions(c *fiber.Ctx) error {
	if s.deps.Grants == nil {
		return fail(c, fiber.StatusServiceUnavailable, protocol.ErrCodeInternal, "izin sistemi yapılandırılmamış")
	}
	if !s.isAdmin(c) {
		return fail(c, fiber.StatusNotFound, protocol.ErrCodeNotFound, "bulunamadı")
	}
	policy, summary, err := parsePolicy(c)
	if err != nil {
		return fail(c, fiber.StatusBadRequest, protocol.ErrCodeBadRequest, err.Error())
	}
	userID := s.currentUser(c)
	s.deps.Grants.SetPolicy(userID, policy)
	s.auditSuccess(userID, "permissions.update", summary)
	return s.getPermissions(c)
}

// parsePolicy istek gövdesini tool.Policy'ye çevirir (izin adları doğrulanır).
func parsePolicy(c *fiber.Ctx) (tool.Policy, string, error) {
	var in policyRequest
	if err := c.BodyParser(&in); err != nil {
		return tool.Policy{}, "", errors.New("geçersiz istek gövdesi")
	}
	grants, err := tool.PermissionsOf(in.Grants)
	if err != nil {
		return tool.Policy{}, "", err
	}
	return tool.Policy{
		Grants: grants, AllowHighRisk: in.AllowHighRisk, AllowCritical: in.AllowCritical,
	}, policySummary(grants, in), nil
}

// ---- Yönetici uçları (yalnızca admin; üyeye 404) ----

// adminUserRow, yönetici ekranındaki bir üye satırıdır: hesap bilgisi + araç politikası.
// Parola özeti HİÇBİR zaman buradan çıkmaz (user.UserView zaten taşımaz).
type adminUserRow struct {
	user.UserView
	Policy tool.PolicyView `json:"policy"`
	// Custom üyeye özel politika verilmiş mi (false = varsayılan politika geçerli).
	Custom bool `json:"custom"`
}

func (s *Server) adminRow(u *user.User) adminUserRow {
	return adminUserRow{
		UserView: u.View(),
		Policy:   s.deps.Grants.PolicyFor(u.ID).View(),
		Custom:   policyScope(s.deps.Grants, u.ID) == "user",
	}
}

// adminAvailable, yönetici uçlarının gerektirdiği servisler yoksa 503 döner.
func (s *Server) adminAvailable(c *fiber.Ctx) error {
	if s.deps.Users == nil || s.deps.Grants == nil {
		return fail(c, fiber.StatusServiceUnavailable, protocol.ErrCodeInternal, "yönetim için hesap ve izin sistemi gerekli")
	}
	return c.Next()
}

// adminListUsers GET /api/admin/users — üyeler ve araç politikaları.
func (s *Server) adminListUsers(c *fiber.Ctx) error {
	list, err := s.deps.Users.List(c.Context(), 500)
	if err != nil {
		return failDomain(c, err)
	}
	rows := make([]adminUserRow, 0, len(list))
	for _, u := range list {
		rows = append(rows, s.adminRow(u))
	}
	return c.JSON(fiber.Map{
		"users": rows,
		"known": knownPermissions,
	})
}

// adminSetPermissions PUT /api/admin/users/:id/permissions — bir üyenin tool politikasını ayarlar.
// Politika YALNIZCA o üyeye yazılır; yöneticinin kendi politikası değişmez.
func (s *Server) adminSetPermissions(c *fiber.Ctx) error {
	target, err := s.deps.Users.ByID(c.Context(), strings.TrimSpace(c.Params("id")))
	if err != nil {
		if errors.Is(err, user.ErrNotFound) {
			return fail(c, fiber.StatusNotFound, protocol.ErrCodeNotFound, "üye bulunamadı")
		}
		return failDomain(c, err)
	}
	policy, summary, perr := parsePolicy(c)
	if perr != nil {
		return fail(c, fiber.StatusBadRequest, protocol.ErrCodeBadRequest, perr.Error())
	}
	s.deps.Grants.SetPolicy(target.ID, policy)
	s.auditSuccess(s.currentUser(c), "permissions.update", "target="+target.Username+" "+summary)
	return c.JSON(fiber.Map{"user": s.adminRow(target)})
}

// policyScope politikanın kullanıcıya özel mi yoksa varsayılan mı olduğunu söyler.
func policyScope(store *tool.GrantStore, userID string) string {
	for _, u := range store.Users() {
		if u == strings.TrimSpace(userID) {
			return "user"
		}
	}
	return "default"
}

func policySummary(grants tool.Permission, in policyRequest) string {
	b := "grants=" + grants.String()
	if in.AllowHighRisk {
		b += " highRisk"
	}
	if in.AllowCritical {
		b += " critical"
	}
	return b
}
