package handlers

import (
	"aggios-app/backend/internal/domain"
	"aggios-app/backend/internal/repository"
	"aggios-app/backend/internal/api/middleware"
	"crypto/rand"
	"database/sql"
	"encoding/hex"
	"encoding/json"
	"log"
	"net/http"
	"regexp"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/gorilla/mux"
	"golang.org/x/crypto/bcrypt"
)

type CRMHandler struct {
	repo *repository.CRMRepository
}

func NewCRMHandler(repo *repository.CRMRepository) *CRMHandler {
	return &CRMHandler{repo: repo}
}

// ==================== CUSTOMERS ====================

type publicRegisterRequest struct {
	domain.CRMCustomer
	Password string `json:"password"`
}

// PublicRegisterCustomer allows public registration without authentication
// SECURITY: Rate limited, validates tenant exists, checks email format, prevents duplicates
func (h *CRMHandler) PublicRegisterCustomer(w http.ResponseWriter, r *http.Request) {
	var req publicRegisterRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Invalid request body",
			"message": err.Error(),
		})
		return
	}

	customer := req.CRMCustomer
	password := req.Password

	// SECURITY 0: Validar força da senha
	if password == "" || len(password) < 8 {
		log.Printf("⚠️ Public registration blocked: invalid password length")
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "invalid_password",
			"message": "A senha deve ter no mínimo 8 caracteres.",
		})
		return
	}

	// Validar complexidade da senha
	hasUpper := false
	hasLower := false
	hasNumber := false
	
	for _, char := range password {
		switch {
		case char >= 'A' && char <= 'Z':
			hasUpper = true
		case char >= 'a' && char <= 'z':
			hasLower = true
		case char >= '0' && char <= '9':
			hasNumber = true
		}
	}

	if !hasUpper || !hasLower || !hasNumber {
		log.Printf("⚠️ Public registration blocked: weak password (missing character types)")
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "weak_password",
			"message": "A senha deve conter pelo menos uma letra maiúscula, uma minúscula e um número.",
		})
		return
	}

	// SECURITY 1: Validar tenant_id obrigatório
	if customer.TenantID == "" {
		log.Printf("⚠️ Public registration blocked: missing tenant_id")
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "tenant_id is required",
		})
		return
	}

	// SECURITY 2: Validar que o tenant existe no banco
	tenantExists, err := h.repo.TenantExists(customer.TenantID)
	if err != nil {
		log.Printf("❌ Error checking tenant existence: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Internal server error",
		})
		return
	}
	if !tenantExists {
		log.Printf("🚫 Public registration blocked: invalid tenant_id=%s", customer.TenantID)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Invalid agency",
		})
		return
	}

	// SECURITY 3: Validar campos obrigatórios
	if customer.Name == "" || customer.Email == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "name and email are required",
		})
		return
	}

	// SECURITY 4: Validar formato de email
	email := strings.TrimSpace(strings.ToLower(customer.Email))
	emailRegex := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
	if !emailRegex.MatchString(email) {
		log.Printf("⚠️ Public registration blocked: invalid email format=%s", email)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Invalid email format",
		})
		return
	}
	customer.Email = email

	// SECURITY 5: Verificar se email já existe para este tenant (constraint unique_email_per_tenant)
	existingCustomer, err := h.repo.GetCustomerByEmailAndTenant(email, customer.TenantID)
	if err == nil && existingCustomer != nil {
		log.Printf("⚠️ Public registration blocked: email already exists for tenant (tenant=%s, email=%s)", customer.TenantID, email)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusConflict)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "duplicate_email",
			"message": "Já existe uma conta cadastrada com este e-mail.",
		})
		return
	}

	// SECURITY 6: Verificar duplicidade de CPF/CNPJ nos notes (formato JSON)
	if customer.Notes != "" {
		log.Printf("🔍 Public registration: checking notes for logo/cpf/cnpj: %s", customer.Notes)
		var notesData map[string]interface{}
		if err := json.Unmarshal([]byte(customer.Notes), &notesData); err == nil {
			// Extrair CPF ou CNPJ
			cpf, hasCPF := notesData["cpf"].(string)
			cnpj, hasCNPJ := notesData["cnpj"].(string)

			// Verificar CPF duplicado
			if hasCPF && cpf != "" {
				existing, err := h.repo.GetCustomerByCPF(cpf, customer.TenantID)
				if err == nil && existing != nil {
					log.Printf("⚠️ Public registration blocked: CPF already exists (tenant=%s, cpf=%s)", customer.TenantID, cpf)
					w.Header().Set("Content-Type", "application/json")
					w.WriteHeader(http.StatusConflict)
					json.NewEncoder(w).Encode(map[string]string{
						"error": "duplicate_email",
						"message": "Já existe uma conta cadastrada com este CPF.",
					})
					return
				}
			}

			// Verificar CNPJ duplicado
			if hasCNPJ && cnpj != "" {
				existing, err := h.repo.GetCustomerByCNPJ(cnpj, customer.TenantID)
				if err == nil && existing != nil {
					log.Printf("⚠️ Public registration blocked: CNPJ already exists (tenant=%s, cnpj=%s)", customer.TenantID, cnpj)
					w.Header().Set("Content-Type", "application/json")
					w.WriteHeader(http.StatusConflict)
					json.NewEncoder(w).Encode(map[string]string{
						"error": "duplicate_email",
						"message": "Já existe uma conta cadastrada com este CNPJ.",
					})
					return
				}
			}

			// Extrair logo se existir
			if logo, hasLogo := notesData["logo_path"].(string); hasLogo && logo != "" {
				log.Printf("🖼️ Found logo in public registration notes: %s", logo)
				customer.LogoURL = logo
			}
		} else {
			log.Printf("⚠️ Failed to unmarshal public registration notes: %v", err)
		}
	}

	// SECURITY 7: Sanitizar nome
	customer.Name = strings.TrimSpace(customer.Name)
	if len(customer.Name) > 255 {
		customer.Name = customer.Name[:255]
	}

	// SECURITY 8: Hash da senha fornecida pelo cliente
	passwordHash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		log.Printf("❌ Error hashing password: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Internal server error",
		})
		return
	}

	customer.ID = uuid.New().String()
	customer.IsActive = true
	// CreatedBy fica vazio pois é cadastro público

	// Garantir que as tags de cadastro público sejam aplicadas
	if customer.Tags == nil || len(customer.Tags) == 0 {
		customer.Tags = []string{"cadastro_publico", "pendente_aprovacao"}
	} else {
		// Garantir que tenha pelo menos a tag cadastro_publico
		hasPublicTag := false
		hasPendingTag := false
		for _, tag := range customer.Tags {
			if tag == "cadastro_publico" {
				hasPublicTag = true
			}
			if tag == "pendente_aprovacao" {
				hasPendingTag = true
			}
		}
		if !hasPublicTag {
			customer.Tags = append(customer.Tags, "cadastro_publico")
		}
		if !hasPendingTag {
			customer.Tags = append(customer.Tags, "pendente_aprovacao")
		}
	}

	log.Printf("📝 Public customer registration: tenant_id=%s, email=%s, name=%s, tags=%v", customer.TenantID, email, customer.Name, customer.Tags)

	if err := h.repo.CreateCustomer(&customer); err != nil {
		log.Printf("❌ Error creating public customer: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to create customer",
			"message": err.Error(),
		})
		return
	}

	// Salvar senha hasheada
	if err := h.repo.UpdateCustomerPassword(customer.ID, string(passwordHash)); err != nil {
		log.Printf("⚠️ Error saving password for customer %s: %v", customer.ID, err)
		// Não retornar erro pois o cliente foi criado, senha pode ser resetada depois
	}

	log.Printf("✅ Public customer created successfully with password: id=%s", customer.ID)

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(map[string]interface{}{
		"customer": customer,
	})
}

func (h *CRMHandler) CreateCustomer(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	userID, _ := r.Context().Value(middleware.UserIDKey).(string)
	
	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Missing tenant_id",
		})
		return
	}

	var customer domain.CRMCustomer
	if err := json.NewDecoder(r.Body).Decode(&customer); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Invalid request body",
			"message": err.Error(),
		})
		return
	}

	customer.ID = uuid.New().String()
	customer.TenantID = tenantID
	customer.CreatedBy = userID
	customer.IsActive = true

	log.Printf("➕ CreateCustomer called: name=%s, company=%s, logo_url=%s", customer.Name, customer.Company, customer.LogoURL)

	if err := h.repo.CreateCustomer(&customer); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to create customer",
			"message": err.Error(),
		})
		return
	}

	// Auto-create a default campaign for this customer
	defaultCampaign := domain.CRMList{
		ID:          uuid.New().String(),
		TenantID:    tenantID,
		CustomerID:  &customer.ID,
		Name:        "Geral - " + customer.Name,
		Description: "Campanha padrão para " + customer.Name,
		Color:       "#3b82f6",
		CreatedBy:   userID,
	}
	_ = h.repo.CreateList(&defaultCampaign)

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(map[string]interface{}{
		"customer": customer,
	})
}

func (h *CRMHandler) GetCustomers(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	
	log.Printf("GetCustomers: tenantID=%s", tenantID)
	
	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Missing tenant_id",
		})
		return
	}

	customers, err := h.repo.GetCustomersByTenant(tenantID)
	log.Printf("GetCustomers: found %d customers, error: %v", len(customers), err)
	if err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to fetch customers",
			"message": err.Error(),
		})
		return
	}

	if customers == nil {
		customers = []domain.CRMCustomer{}
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"customers": customers,
	})
}

func (h *CRMHandler) GetCustomer(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	
	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Missing tenant_id",
		})
		return
	}
	
	vars := mux.Vars(r)
	customerID := vars["id"]

	customer, err := h.repo.GetCustomerByID(customerID, tenantID)
	if err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusNotFound)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Customer not found",
			"message": err.Error(),
		})
		return
	}

	// Buscar listas do cliente
	lists, _ := h.repo.GetCustomerLists(customerID)
	if lists == nil {
		lists = []domain.CRMList{}
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"customer": customer,
		"lists":    lists,
	})
}

func (h *CRMHandler) UpdateCustomer(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	
	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Missing tenant_id",
		})
		return
	}
	
	vars := mux.Vars(r)
	customerID := vars["id"]

	var customer domain.CRMCustomer
	if err := json.NewDecoder(r.Body).Decode(&customer); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Invalid request body",
			"message": err.Error(),
		})
		return
	}

	customer.ID = customerID
	customer.TenantID = tenantID

	log.Printf("🔄 UpdateCustomer called for customer %s, name: %s, logo_url: %s, tags: %v", customerID, customer.Name, customer.LogoURL, customer.Tags)

	if err := h.repo.UpdateCustomer(&customer); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to update customer",
			"message": err.Error(),
		})
		return
	}

	// Se as tags foram alteradas e a tag pendente_aprovacao foi removida, liberar acesso ao portal
	hasPendingTag := false
	for _, tag := range customer.Tags {
		if tag == "pendente_aprovacao" {
			hasPendingTag = true
			break
		}
	}

	log.Printf("🔍 Checking portal access: hasPendingTag=%v", hasPendingTag)

	// Se não tem mais a tag pendente e tinha senha definida, liberar acesso
	if !hasPendingTag {
		existingCustomer, err := h.repo.GetCustomerByID(customerID, tenantID)
		log.Printf("🔍 Existing customer check: hasPassword=%v, err=%v", existingCustomer != nil && existingCustomer.PasswordHash != "", err)
		
		if err == nil && existingCustomer.PasswordHash != "" {
			// Liberar acesso ao portal
			if err := h.repo.EnableCustomerPortalAccess(customerID); err != nil {
				log.Printf("⚠️ Warning: Failed to enable portal access for customer %s: %v", customerID, err)
			} else {
				log.Printf("✅ Portal access enabled for customer %s", customerID)
			}
		} else if err == nil && existingCustomer.PasswordHash == "" {
			log.Printf("⚠️ Customer %s approved but has no password yet - portal access will be enabled when password is set", customerID)
		}
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{
		"message": "Customer updated successfully",
	})
}

func (h *CRMHandler) DeleteCustomer(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	
	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Missing tenant_id",
		})
		return
	}
	
	vars := mux.Vars(r)
	customerID := vars["id"]

	if err := h.repo.DeleteCustomer(customerID, tenantID); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to delete customer",
			"message": err.Error(),
		})
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{
		"message": "Customer deleted successfully",
	})
}

// ==================== LISTS ====================

func (h *CRMHandler) CreateList(w http.ResponseWriter, r *http.Request) {
	tenantIDVal := r.Context().Value(middleware.TenantIDKey)
	userIDVal := r.Context().Value(middleware.UserIDKey)
	
	log.Printf("🔍 CreateList DEBUG: tenantID type=%T value=%v | userID type=%T value=%v", 
		tenantIDVal, tenantIDVal, userIDVal, userIDVal)
	
	tenantID, ok := tenantIDVal.(string)
	if !ok || tenantID == "" {
		log.Printf("❌ CreateList: Missing or invalid tenant_id")
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Missing tenant_id",
		})
		return
	}
	
	userID, _ := userIDVal.(string)

	var list domain.CRMList
	if err := json.NewDecoder(r.Body).Decode(&list); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Invalid request body",
			"message": err.Error(),
		})
		return
	}

	list.ID = uuid.New().String()
	list.TenantID = tenantID
	list.CreatedBy = userID

	// Handle empty customer_id
	if list.CustomerID != nil && *list.CustomerID == "" {
		list.CustomerID = nil
	}

	if list.Color == "" {
		list.Color = "#3b82f6"
	}

	if err := h.repo.CreateList(&list); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to create list",
			"message": err.Error(),
		})
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(map[string]interface{}{
		"list": list,
	})
}

func (h *CRMHandler) GetLists(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	
	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Missing tenant_id",
		})
		return
	}

	customerID := r.URL.Query().Get("customer_id")
	log.Printf("GetLists: tenantID=%s, customerID=%s", tenantID, customerID)

	lists, err := h.repo.GetListsByTenant(tenantID)
	if err != nil {
		log.Printf("GetLists: Error fetching lists: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to fetch lists",
			"message": err.Error(),
		})
		return
	}

	if lists == nil {
		lists = []domain.CRMListWithCustomers{}
	}

	// Filter by customer if provided
	if customerID != "" {
		filteredLists := []domain.CRMListWithCustomers{}
		for _, list := range lists {
			if list.CustomerID != nil && *list.CustomerID == customerID {
				filteredLists = append(filteredLists, list)
			}
		}
		log.Printf("GetLists: Filtered lists from %d to %d", len(lists), len(filteredLists))
		lists = filteredLists
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"lists": lists,
	})
}

func (h *CRMHandler) GetList(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	
	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Missing tenant_id",
		})
		return
	}
	
	vars := mux.Vars(r)
	listID := vars["id"]

	list, err := h.repo.GetListByID(listID, tenantID)
	if err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusNotFound)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "List not found",
			"message": err.Error(),
		})
		return
	}

	// Buscar clientes da lista
	customers, _ := h.repo.GetListCustomers(listID, tenantID)
	if customers == nil {
		customers = []domain.CRMCustomer{}
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"list":      list,
		"customers": customers,
	})
}

func (h *CRMHandler) UpdateList(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	
	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Missing tenant_id",
		})
		return
	}
	
	vars := mux.Vars(r)
	listID := vars["id"]

	var list domain.CRMList
	if err := json.NewDecoder(r.Body).Decode(&list); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Invalid request body",
			"message": err.Error(),
		})
		return
	}

	list.ID = listID
	list.TenantID = tenantID

	// Handle empty customer_id
	if list.CustomerID != nil && *list.CustomerID == "" {
		list.CustomerID = nil
	}

	if err := h.repo.UpdateList(&list); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to update list",
			"message": err.Error(),
		})
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{
		"message": "List updated successfully",
	})
}

func (h *CRMHandler) DeleteList(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	
	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Missing tenant_id",
		})
		return
	}
	
	vars := mux.Vars(r)
	listID := vars["id"]

	if err := h.repo.DeleteList(listID, tenantID); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to delete list",
			"message": err.Error(),
		})
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{
		"message": "List deleted successfully",
	})
}

// ==================== LEADS ====================

func (h *CRMHandler) CreateLead(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	userID, _ := r.Context().Value(middleware.UserIDKey).(string)

	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{"error": "Missing tenant_id"})
		return
	}

	var lead domain.CRMLead
	if err := json.NewDecoder(r.Body).Decode(&lead); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Invalid request body",
			"message": err.Error(),
		})
		return
	}

	lead.ID = uuid.New().String()
	lead.TenantID = tenantID
	lead.CreatedBy = userID
	lead.IsActive = true
	if lead.Status == "" {
		lead.Status = "novo"
	}
	if lead.SourceMeta == nil {
		lead.SourceMeta = json.RawMessage(`{}`)
	}

	// Ensure default funnel and stage if not provided
	if lead.FunnelID == nil || *lead.FunnelID == "" || lead.StageID == nil || *lead.StageID == "" {
		funnelID, err := h.repo.EnsureDefaultFunnel(tenantID)
		if err == nil {
			lead.FunnelID = &funnelID
			stages, err := h.repo.GetStagesByFunnelID(funnelID)
			if err == nil && len(stages) > 0 {
				lead.StageID = &stages[0].ID
			}
		}
	}

	if err := h.repo.CreateLead(&lead); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to create lead",
			"message": err.Error(),
		})
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(map[string]interface{}{
		"lead": lead,
	})
}

func (h *CRMHandler) GetLeads(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{"error": "Missing tenant_id"})
		return
	}

	customerID := r.URL.Query().Get("customer_id")
	log.Printf("GetLeads: tenantID=%s, customerID=%s", tenantID, customerID)

	leads, err := h.repo.GetLeadsWithListsByTenant(tenantID)
	if err != nil {
		log.Printf("GetLeads: Error fetching leads: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to fetch leads",
			"message": err.Error(),
		})
		return
	}
	if leads == nil {
		leads = []domain.CRMLeadWithLists{}
	}

	// Filter by customer if provided
	if customerID != "" {
		filteredLeads := []domain.CRMLeadWithLists{}
		for _, lead := range leads {
			if lead.CustomerID != nil && *lead.CustomerID == customerID {
				filteredLeads = append(filteredLeads, lead)
			}
		}
		log.Printf("GetLeads: Filtered leads for customer %s: from %d to %d", customerID, len(leads), len(filteredLeads))
		leads = filteredLeads
	} else {
		log.Printf("GetLeads: No customer_id filter applied, returning all %d leads", len(leads))
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"leads": leads,
	})
}

func (h *CRMHandler) GetLeadsByList(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	listID := vars["id"]

	leads, err := h.repo.GetLeadsByListID(listID)
	if err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to fetch leads",
			"message": err.Error(),
		})
		return
	}
	if leads == nil {
		leads = []domain.CRMLead{}
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"leads": leads,
	})
}

func (h *CRMHandler) GetLead(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{"error": "Missing tenant_id"})
		return
	}

	vars := mux.Vars(r)
	leadID := vars["id"]

	lead, err := h.repo.GetLeadByID(leadID, tenantID)
	if err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusNotFound)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Lead not found",
			"message": err.Error(),
		})
		return
	}

	lists, _ := h.repo.GetLeadLists(leadID)
	if lists == nil {
		lists = []domain.CRMList{}
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"lead":  lead,
		"lists": lists,
	})
}

func (h *CRMHandler) UpdateLead(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{"error": "Missing tenant_id"})
		return
	}

	vars := mux.Vars(r)
	leadID := vars["id"]

	var lead domain.CRMLead
	if err := json.NewDecoder(r.Body).Decode(&lead); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Invalid request body",
			"message": err.Error(),
		})
		return
	}

	lead.ID = leadID
	lead.TenantID = tenantID
	if lead.SourceMeta == nil {
		lead.SourceMeta = json.RawMessage(`{}`)
	}

	if err := h.repo.UpdateLead(&lead); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to update lead",
			"message": err.Error(),
		})
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{"message": "Lead updated successfully"})
}

func (h *CRMHandler) DeleteLead(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{"error": "Missing tenant_id"})
		return
	}

	vars := mux.Vars(r)
	leadID := vars["id"]

	if err := h.repo.DeleteLead(leadID, tenantID); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to delete lead",
			"message": err.Error(),
		})
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{"message": "Lead deleted successfully"})
}

func (h *CRMHandler) AddLeadToList(w http.ResponseWriter, r *http.Request) {
	userID, _ := r.Context().Value(middleware.UserIDKey).(string)
	vars := mux.Vars(r)
	leadID := vars["lead_id"]
	listID := vars["list_id"]

	if err := h.repo.AddLeadToList(leadID, listID, userID); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to add lead to list",
			"message": err.Error(),
		})
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{"message": "Lead added to list successfully"})
}

func (h *CRMHandler) RemoveLeadFromList(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	leadID := vars["lead_id"]
	listID := vars["list_id"]

	if err := h.repo.RemoveLeadFromList(leadID, listID); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to remove lead from list",
			"message": err.Error(),
		})
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{"message": "Lead removed from list successfully"})
}

type LeadIngestRequest struct {
	Name       string                 `json:"name"`
	Email      string                 `json:"email"`
	Phone      string                 `json:"phone"`
	Notes      string                 `json:"notes"`
	Tags       []string               `json:"tags"`
	Source     string                 `json:"source"`
	SourceMeta map[string]interface{} `json:"source_meta"`
	ListID     string                 `json:"list_id"`
	ListName   string                 `json:"list_name"`
	Status     string                 `json:"status"`
	CustomerID string                 `json:"customer_id"`
}

func (h *CRMHandler) IngestLead(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	userID, _ := r.Context().Value(middleware.UserIDKey).(string)
	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{"error": "Missing tenant_id"})
		return
	}

	var req LeadIngestRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Invalid request body",
			"message": err.Error(),
		})
		return
	}

	if req.Email == "" && req.Phone == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Informe ao menos email ou telefone",
		})
		return
	}

	// Resolver list_id (opcional)
	listID := req.ListID
	if listID == "" && req.ListName != "" {
		if existing, err := h.repo.GetListByName(tenantID, req.ListName); err == nil {
			listID = existing.ID
		} else if err == sql.ErrNoRows {
			newList := domain.CRMList{
				ID:          uuid.New().String(),
				TenantID:    tenantID,
				Name:        req.ListName,
				Description: "Criada automaticamente via ingestão de leads",
				Color:       "#3b82f6",
				CreatedBy:   userID,
			}
			if err := h.repo.CreateList(&newList); err == nil {
				listID = newList.ID
			}
		}
	}

	// Dedup por email/phone
	var existingLead *domain.CRMLead
	if found, err := h.repo.GetLeadByEmailOrPhone(tenantID, req.Email, req.Phone); err == nil {
		existingLead = found
	} else if err != nil && err != sql.ErrNoRows {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to ingest lead",
			"message": err.Error(),
		})
		return
	}

	// Normalizar source_meta
	sourceMetaBytes, _ := json.Marshal(req.SourceMeta)
	if len(sourceMetaBytes) == 0 {
		sourceMetaBytes = []byte(`{}`)
	}

	if req.Status == ""	{
		req.Status = "novo"
	}
	if req.Source == "" {
		req.Source = "import"
	}

	// Processar customer_id
	var customerIDPtr *string
	if req.CustomerID != "" {
		customerIDPtr = &req.CustomerID
	}

	if existingLead == nil {
		lead := domain.CRMLead{
			ID:         uuid.New().String(),
			TenantID:   tenantID,
			CustomerID: customerIDPtr,
			Name:       req.Name,
			Email:      req.Email,
			Phone:      req.Phone,
			Source:     req.Source,
			SourceMeta: json.RawMessage(sourceMetaBytes),
			Status:     req.Status,
			Notes:      req.Notes,
			Tags:       req.Tags,
			IsActive:   true,
			CreatedBy:  userID,
		}

		if err := h.repo.CreateLead(&lead); err != nil {
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(http.StatusInternalServerError)
			json.NewEncoder(w).Encode(map[string]string{
				"error":   "Failed to ingest lead",
				"message": err.Error(),
			})
			return
		}

		if listID != "" {
			_ = h.repo.AddLeadToList(lead.ID, listID, userID)
		}

		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusCreated)
		json.NewEncoder(w).Encode(map[string]interface{}{
			"lead":        lead,
			"created":     true,
			"linked_list": listID != "",
		})
		return
	}

	// Se já existe: atualiza campos básicos se vierem preenchidos
	updated := *existingLead
	if customerIDPtr != nil {
		updated.CustomerID = customerIDPtr
	}
	if updated.Name == "" && req.Name != "" {
		updated.Name = req.Name
	}
	if updated.Email == "" && req.Email != "" {
		updated.Email = req.Email
	}
	if updated.Phone == "" && req.Phone != "" {
		updated.Phone = req.Phone
	}
	updated.Source = req.Source
	updated.SourceMeta = json.RawMessage(sourceMetaBytes)
	if updated.Status == "" {
		updated.Status = req.Status
	}
	if req.Notes != "" {
		updated.Notes = req.Notes
	}
	if len(req.Tags) > 0 {
		updated.Tags = req.Tags
	}
	if err := h.repo.UpdateLead(&updated); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to update existing lead",
			"message": err.Error(),
		})
		return
	}
	if listID != "" {
		_ = h.repo.AddLeadToList(updated.ID, listID, userID)
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"lead":        updated,
		"created":     false,
		"linked_list": listID != "",
	})
}

// ==================== CUSTOMER <-> LIST ====================

func (h *CRMHandler) AddCustomerToList(w http.ResponseWriter, r *http.Request) {
	userID, _ := r.Context().Value(middleware.UserIDKey).(string)
	
	vars := mux.Vars(r)
	customerID := vars["customer_id"]
	listID := vars["list_id"]

	if err := h.repo.AddCustomerToList(customerID, listID, userID); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to add customer to list",
			"message": err.Error(),
		})
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{
		"message": "Customer added to list successfully",
	})
}

func (h *CRMHandler) RemoveCustomerFromList(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	customerID := vars["customer_id"]
	listID := vars["list_id"]

	if err := h.repo.RemoveCustomerFromList(customerID, listID); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to remove customer from list",
			"message": err.Error(),
		})
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{
		"message": "Customer removed from list successfully",
	})
}

// GenerateShareToken gera um token de compartilhamento para visualização de leads de um cliente
func (h *CRMHandler) GenerateShareToken(w http.ResponseWriter, r *http.Request) {
	var req struct {
		CustomerID string `json:"customer_id"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{"error": "Invalid request body"})
		return
	}

	tenantID := r.Header.Get("X-Tenant-Subdomain")
	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{"error": "Tenant ID is required"})
		return
	}

	userID := r.Context().Value("user_id").(string)

	// Gera token seguro
	tokenBytes := make([]byte, 32)
	if _, err := rand.Read(tokenBytes); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{"error": "Failed to generate token"})
		return
	}
	token := hex.EncodeToString(tokenBytes)

	shareToken := domain.CRMShareToken{
		ID:         uuid.New().String(),
		TenantID:   tenantID,
		CustomerID: req.CustomerID,
		Token:      token,
		ExpiresAt:  nil, // Token sem expiração
		CreatedBy:  userID,
		CreatedAt:  time.Now(),
	}

	if err := h.repo.CreateShareToken(&shareToken); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error":   "Failed to create share token",
			"message": err.Error(),
		})
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{
		"token": token,
	})
}

// GetSharedData retorna os dados compartilhados de um cliente via token (endpoint público)
func (h *CRMHandler) GetSharedData(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	token := vars["token"]

	shareToken, err := h.repo.GetShareTokenByToken(token)
	if err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusNotFound)
		json.NewEncoder(w).Encode(map[string]string{"error": "Invalid or expired token"})
		return
	}

	// Verifica se o token expirou
	if shareToken.ExpiresAt != nil && shareToken.ExpiresAt.Before(time.Now()) {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusForbidden)
		json.NewEncoder(w).Encode(map[string]string{"error": "Token expired"})
		return
	}

	// Busca dados do cliente
	customer, err := h.repo.GetCustomerByID(shareToken.CustomerID, shareToken.TenantID)
	if err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusNotFound)
		json.NewEncoder(w).Encode(map[string]string{"error": "Customer not found"})
		return
	}

	// Busca leads do cliente
	leads, err := h.repo.GetLeadsByCustomerID(shareToken.CustomerID)
	if err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{"error": "Failed to fetch leads"})
		return
	}

	// Calcula estatísticas
	stats := calculateLeadStats(leads)

	response := map[string]interface{}{
		"customer": map[string]string{
			"name":    customer.Name,
			"company": customer.Company,
		},
		"leads": leads,
		"stats": stats,
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

func calculateLeadStats(leads []domain.CRMLead) map[string]interface{} {
	stats := map[string]interface{}{
		"total":          len(leads),
		"novo":           0,
		"qualificado":    0,
		"negociacao":     0,
		"convertido":     0,
		"perdido":        0,
		"bySource":       make(map[string]int),
		"conversionRate": 0.0,
		"thisMonth":      0,
		"lastMonth":      0,
	}

	if len(leads) == 0 {
		return stats
	}

	now := time.Now()
	currentMonth := now.Month()
	currentYear := now.Year()
	lastMonth := now.AddDate(0, -1, 0)

	statusCount := make(map[string]int)
	bySource := make(map[string]int)
	thisMonthCount := 0
	lastMonthCount := 0

	for _, lead := range leads {
		// Conta por status
		statusCount[lead.Status]++

		// Conta por origem
		source := lead.Source
		if source == "" {
			source = "manual"
		}
		bySource[source]++

		// Conta por mês
		if lead.CreatedAt.Month() == currentMonth && lead.CreatedAt.Year() == currentYear {
			thisMonthCount++
		}
		if lead.CreatedAt.Month() == lastMonth.Month() && lead.CreatedAt.Year() == lastMonth.Year() {
			lastMonthCount++
		}
	}

	stats["novo"] = statusCount["novo"]
	stats["qualificado"] = statusCount["qualificado"]
	stats["negociacao"] = statusCount["negociacao"]
	stats["convertido"] = statusCount["convertido"]
	stats["perdido"] = statusCount["perdido"]
	stats["bySource"] = bySource
	stats["thisMonth"] = thisMonthCount
	stats["lastMonth"] = lastMonthCount

	// Taxa de conversão
	if len(leads) > 0 {
		conversionRate := (float64(statusCount["convertido"]) / float64(len(leads))) * 100
		stats["conversionRate"] = conversionRate
	}

	return stats
}

// GenerateCustomerPortalAccess gera credenciais de acesso ao portal para um cliente
func (h *CRMHandler) GenerateCustomerPortalAccess(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{"error": "Missing tenant_id"})
		return
	}

	vars := mux.Vars(r)
	customerID := vars["id"]

	var req struct {
		Password string `json:"password"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Password == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{"error": "Password is required"})
		return
	}

	// Verificar se cliente existe
	customer, err := h.repo.GetCustomerByID(customerID, tenantID)
	if err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusNotFound)
		json.NewEncoder(w).Encode(map[string]string{"error": "Customer not found"})
		return
	}

	// Gerar hash da senha
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
	if err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{"error": "Failed to generate password"})
		return
	}

	// Atualizar acesso ao portal
	if err := h.repo.SetCustomerPortalAccess(customerID, string(hashedPassword), true); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{"error": "Failed to set portal access"})
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"message": "Portal access granted",
		"email":   customer.Email,
	})
}

// GetDashboard returns stats for the CRM dashboard
func (h *CRMHandler) GetDashboard(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{"error": "Missing tenant_id"})
		return
	}

	customerID := r.URL.Query().Get("customer_id")
	log.Printf("GetDashboard: tenantID=%s, customerID=%s", tenantID, customerID)

	// Get all leads for stats
	leads, err := h.repo.GetLeadsByTenant(tenantID)
	if err != nil {
		log.Printf("GetDashboard: Error fetching leads: %v", err)
		leads = []domain.CRMLead{}
	}

	// Get all customers for stats
	customers, err := h.repo.GetCustomersByTenant(tenantID)
	if err != nil {
		log.Printf("GetDashboard: Error fetching customers: %v", err)
		customers = []domain.CRMCustomer{}
	}

	// Get all lists (campaigns)
	lists, err := h.repo.GetListsByTenant(tenantID)
	if err != nil {
		log.Printf("GetDashboard: Error fetching lists: %v", err)
	}

	// Filter by customer if provided
	if customerID != "" {
		filteredLeads := []domain.CRMLead{}
		for _, lead := range leads {
			if lead.CustomerID != nil && *lead.CustomerID == customerID {
				filteredLeads = append(filteredLeads, lead)
			}
		}
		log.Printf("GetDashboard: Filtered leads from %d to %d", len(leads), len(filteredLeads))
		leads = filteredLeads

		filteredLists := []domain.CRMListWithCustomers{}
		for _, list := range lists {
			if list.CustomerID != nil && *list.CustomerID == customerID {
				filteredLists = append(filteredLists, list)
			}
		}
		log.Printf("GetDashboard: Filtered lists from %d to %d", len(lists), len(filteredLists))
		lists = filteredLists
	}

	stats := calculateLeadStats(leads)
	stats["total_customers"] = len(customers)
	if customerID != "" {
		stats["total_customers"] = 1 // If filtered by customer, we only care about that one
	}
	stats["total_campaigns"] = len(lists)

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"stats": stats,
	})
}

// ImportLeads handles bulk lead import from JSON
func (h *CRMHandler) ImportLeads(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	userID, _ := r.Context().Value(middleware.UserIDKey).(string)

	if tenantID == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{"error": "Missing tenant_id"})
		return
	}

	var req struct {
		CampaignID string           `json:"campaign_id"`
		CustomerID string           `json:"customer_id"`
		Leads      []domain.CRMLead `json:"leads"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		log.Printf("ImportLeads: Error decoding body: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{"error": "Invalid request body"})
		return
	}

	log.Printf("ImportLeads: Received %d leads for campaign %s and customer %s", len(req.Leads), req.CampaignID, req.CustomerID)

	if len(req.Leads) == 0 {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{"error": "No leads provided"})
		return
	}

	// Get default funnel and stage
	var defaultFunnelID string
	var defaultStageID string
	funnelID, err := h.repo.EnsureDefaultFunnel(tenantID)
	if err == nil {
		defaultFunnelID = funnelID
		stages, err := h.repo.GetStagesByFunnelID(funnelID)
		if err == nil && len(stages) > 0 {
			defaultStageID = stages[0].ID
		}
	}

	// Prepare leads for bulk insert
	now := time.Now()
	for i := range req.Leads {
		if req.Leads[i].ID == "" {
			req.Leads[i].ID = uuid.New().String()
		}
		req.Leads[i].TenantID = tenantID
		req.Leads[i].CreatedBy = userID
		req.Leads[i].CreatedAt = now
		req.Leads[i].UpdatedAt = now
		req.Leads[i].IsActive = true
		if req.Leads[i].Status == "" {
			req.Leads[i].Status = "novo"
		}
		if req.Leads[i].Source == "" {
			req.Leads[i].Source = "import"
		}
		if len(req.Leads[i].SourceMeta) == 0 {
			req.Leads[i].SourceMeta = json.RawMessage("{}")
		}

		// Assign default funnel and stage if not provided
		if (req.Leads[i].FunnelID == nil || *req.Leads[i].FunnelID == "") && defaultFunnelID != "" {
			req.Leads[i].FunnelID = &defaultFunnelID
		}
		if (req.Leads[i].StageID == nil || *req.Leads[i].StageID == "") && defaultStageID != "" {
			req.Leads[i].StageID = &defaultStageID
		}

		log.Printf("Lead %d: SourceMeta='%s'", i, string(req.Leads[i].SourceMeta))
		// If a customer_id was provided in the request, use it for all leads
		if req.CustomerID != "" {
			customerID := req.CustomerID
			req.Leads[i].CustomerID = &customerID
		}
	}

	// Bulk insert leads
	if err := h.repo.BulkCreateLeads(req.Leads); err != nil {
		log.Printf("ImportLeads: Error in BulkCreateLeads: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{"error": "Failed to import leads", "details": err.Error()})
		return
	}

	log.Printf("ImportLeads: Successfully created %d leads", len(req.Leads))

	// If a campaign_id was provided, link all leads to it
	if req.CampaignID != "" {
		leadIDs := make([]string, len(req.Leads))
		for i, lead := range req.Leads {
			leadIDs[i] = lead.ID
		}
		if err := h.repo.BulkAddLeadsToList(leadIDs, req.CampaignID, userID); err != nil {
			log.Printf("ImportLeads: Error in BulkAddLeadsToList: %v", err)
		}
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(map[string]interface{}{
		"message": "Leads imported successfully",
		"count":   len(req.Leads),
	})
}

// ==================== FUNNELS & STAGES ====================

func (h *CRMHandler) ListFunnels(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	if tenantID == "" {
		http.Error(w, "Missing tenant_id", http.StatusBadRequest)
		return
	}

	funnels, err := h.repo.GetFunnelsByTenant(tenantID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// If no funnels, ensure default exists
	if len(funnels) == 0 {
		_, err := h.repo.EnsureDefaultFunnel(tenantID)
		if err == nil {
			funnels, _ = h.repo.GetFunnelsByTenant(tenantID)
		}
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{"funnels": funnels})
}

func (h *CRMHandler) GetFunnel(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	if tenantID == "" {
		http.Error(w, "Missing tenant_id", http.StatusBadRequest)
		return
	}

	vars := mux.Vars(r)
	id := vars["id"]

	funnel, err := h.repo.GetFunnelByID(id, tenantID)
	if err != nil {
		http.Error(w, "Funnel not found", http.StatusNotFound)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{"funnel": funnel})
}

func (h *CRMHandler) CreateFunnel(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	var funnel domain.CRMFunnel
	if err := json.NewDecoder(r.Body).Decode(&funnel); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	funnel.ID = uuid.New().String()
	funnel.TenantID = tenantID

	if err := h.repo.CreateFunnel(&funnel); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(funnel)
}

func (h *CRMHandler) UpdateFunnel(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	vars := mux.Vars(r)
	id := vars["id"]

	var funnel domain.CRMFunnel
	if err := json.NewDecoder(r.Body).Decode(&funnel); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	funnel.ID = id
	funnel.TenantID = tenantID

	if err := h.repo.UpdateFunnel(&funnel); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (h *CRMHandler) DeleteFunnel(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	vars := mux.Vars(r)
	id := vars["id"]

	if err := h.repo.DeleteFunnel(id, tenantID); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (h *CRMHandler) ListStages(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	funnelID := vars["funnelId"]

	stages, err := h.repo.GetStagesByFunnelID(funnelID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{"stages": stages})
}

func (h *CRMHandler) CreateStage(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	funnelID := vars["funnelId"]

	var stage domain.CRMFunnelStage
	if err := json.NewDecoder(r.Body).Decode(&stage); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	stage.ID = uuid.New().String()
	stage.FunnelID = funnelID

	if err := h.repo.CreateFunnelStage(&stage); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(stage)
}

func (h *CRMHandler) UpdateStage(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]

	var stage domain.CRMFunnelStage
	if err := json.NewDecoder(r.Body).Decode(&stage); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	stage.ID = id

	if err := h.repo.UpdateFunnelStage(&stage); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (h *CRMHandler) DeleteStage(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]

	if err := h.repo.DeleteFunnelStage(id); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

func (h *CRMHandler) UpdateLeadStage(w http.ResponseWriter, r *http.Request) {
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	vars := mux.Vars(r)
	leadID := vars["leadId"]

	var req struct {
		FunnelID string `json:"funnel_id"`
		StageID  string `json:"stage_id"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if err := h.repo.UpdateLeadStage(leadID, tenantID, req.FunnelID, req.StageID); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}


