package handler

import (
	"baron-sso-backend/internal/domain"
	"errors"
	"strings"
	"time"

	"github.com/gofiber/fiber/v2"
	"gorm.io/gorm"
)

type TenantHandler struct {
	DB *gorm.DB
}

func NewTenantHandler(db *gorm.DB) *TenantHandler {
	return &TenantHandler{DB: db}
}

type tenantSummary struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Slug        string `json:"slug"`
	Description string `json:"description"`
	Status      string `json:"status"`
	CreatedAt   string `json:"createdAt"`
	UpdatedAt   string `json:"updatedAt"`
}

type tenantListResponse struct {
	Items  []tenantSummary `json:"items"`
	Limit  int             `json:"limit"`
	Offset int             `json:"offset"`
	Total  int64           `json:"total"`
}

func (h *TenantHandler) ListTenants(c *fiber.Ctx) error {
	if h.DB == nil {
		return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "database not available"})
	}

	limit := c.QueryInt("limit", 50)
	offset := c.QueryInt("offset", 0)
	if limit <= 0 {
		limit = 50
	}
	if offset < 0 {
		offset = 0
	}

	var total int64
	if err := h.DB.Model(&domain.Tenant{}).Count(&total).Error; err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	var tenants []domain.Tenant
	if err := h.DB.Order("created_at desc").Limit(limit).Offset(offset).Find(&tenants).Error; err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	items := make([]tenantSummary, 0, len(tenants))
	for _, t := range tenants {
		items = append(items, mapTenantSummary(t))
	}

	return c.JSON(tenantListResponse{Items: items, Limit: limit, Offset: offset, Total: total})
}

func (h *TenantHandler) GetTenant(c *fiber.Ctx) error {
	if h.DB == nil {
		return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "database not available"})
	}

	tenantID := strings.TrimSpace(c.Params("id"))
	if tenantID == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "tenant id is required"})
	}

	var tenant domain.Tenant
	if err := h.DB.First(&tenant, "id = ?", tenantID).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "tenant not found"})
		}
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	return c.JSON(mapTenantSummary(tenant))
}

func (h *TenantHandler) CreateTenant(c *fiber.Ctx) error {
	if h.DB == nil {
		return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "database not available"})
	}

	var req struct {
		Name        string `json:"name"`
		Slug        string `json:"slug"`
		Description string `json:"description"`
		Status      string `json:"status"`
	}
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid request body"})
	}

	name := strings.TrimSpace(req.Name)
	if name == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "name is required"})
	}

	slug := normalizeTenantSlug(req.Slug)
	if slug == "" {
		slug = normalizeTenantSlug(name)
	}
	if slug == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "slug is required"})
	}

	status := normalizeTenantStatus(req.Status)
	if status == "" {
		status = "active"
	}

	var exists domain.Tenant
	if err := h.DB.Unscoped().Where("slug = ?", slug).First(&exists).Error; err == nil {
		return c.Status(fiber.StatusConflict).JSON(fiber.Map{"error": "slug already exists"})
	} else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	tenant := domain.Tenant{
		Name:        name,
		Slug:        slug,
		Description: strings.TrimSpace(req.Description),
		Status:      status,
	}

	if err := h.DB.Create(&tenant).Error; err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	return c.Status(fiber.StatusCreated).JSON(mapTenantSummary(tenant))
}

func (h *TenantHandler) UpdateTenant(c *fiber.Ctx) error {
	if h.DB == nil {
		return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "database not available"})
	}

	tenantID := strings.TrimSpace(c.Params("id"))
	if tenantID == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "tenant id is required"})
	}

	var tenant domain.Tenant
	if err := h.DB.First(&tenant, "id = ?", tenantID).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "tenant not found"})
		}
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	var req struct {
		Name        *string `json:"name"`
		Slug        *string `json:"slug"`
		Description *string `json:"description"`
		Status      *string `json:"status"`
	}
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid request body"})
	}

	if req.Name != nil {
		name := strings.TrimSpace(*req.Name)
		if name == "" {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "name cannot be empty"})
		}
		tenant.Name = name
	}
	if req.Slug != nil {
		slug := normalizeTenantSlug(*req.Slug)
		if slug == "" {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "slug cannot be empty"})
		}
		if slug != tenant.Slug {
			var exists domain.Tenant
			if err := h.DB.Unscoped().Where("slug = ?", slug).First(&exists).Error; err == nil {
				return c.Status(fiber.StatusConflict).JSON(fiber.Map{"error": "slug already exists"})
			} else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
				return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
			}
			tenant.Slug = slug
		}
	}
	if req.Description != nil {
		tenant.Description = strings.TrimSpace(*req.Description)
	}
	if req.Status != nil {
		status := normalizeTenantStatus(*req.Status)
		if status == "" {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "status must be active or inactive"})
		}
		tenant.Status = status
	}

	if err := h.DB.Save(&tenant).Error; err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	return c.JSON(mapTenantSummary(tenant))
}

func (h *TenantHandler) DeleteTenant(c *fiber.Ctx) error {
	if h.DB == nil {
		return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "database not available"})
	}

	tenantID := strings.TrimSpace(c.Params("id"))
	if tenantID == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "tenant id is required"})
	}

	if err := h.DB.Delete(&domain.Tenant{}, "id = ?", tenantID).Error; err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	return c.SendStatus(fiber.StatusNoContent)
}

func mapTenantSummary(t domain.Tenant) tenantSummary {
	return tenantSummary{
		ID:          t.ID,
		Name:        t.Name,
		Slug:        t.Slug,
		Description: t.Description,
		Status:      t.Status,
		CreatedAt:   t.CreatedAt.Format(time.RFC3339),
		UpdatedAt:   t.UpdatedAt.Format(time.RFC3339),
	}
}

func normalizeTenantSlug(value string) string {
	value = strings.ToLower(strings.TrimSpace(value))
	value = strings.ReplaceAll(value, " ", "-")
	var b strings.Builder
	for _, r := range value {
		if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' {
			b.WriteRune(r)
		}
	}
	return strings.Trim(b.String(), "-")
}

func normalizeTenantStatus(value string) string {
	value = strings.ToLower(strings.TrimSpace(value))
	if value == "" {
		return ""
	}
	if value != "active" && value != "inactive" {
		return ""
	}
	return value
}
