package handlers

import (
	"aggios-app/backend/internal/api/middleware"
	"aggios-app/backend/internal/domain"
	"aggios-app/backend/internal/repository"
	"aggios-app/backend/internal/service"
	"encoding/json"
	"log"
	"net/http"
	"time"

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

// CollaboratorHandler handles agency collaborator management
type CollaboratorHandler struct {
	userRepo    *repository.UserRepository
	agencyServ  *service.AgencyService
}

// NewCollaboratorHandler creates a new collaborator handler
func NewCollaboratorHandler(userRepo *repository.UserRepository, agencyServ *service.AgencyService) *CollaboratorHandler {
	return &CollaboratorHandler{
		userRepo:   userRepo,
		agencyServ: agencyServ,
	}
}

// AddCollaboratorRequest representa a requisição para adicionar um colaborador
type AddCollaboratorRequest struct {
	Email string `json:"email"`
	Name  string `json:"name"`
}

// CollaboratorResponse representa um colaborador
type CollaboratorResponse struct {
	ID                   string     `json:"id"`
	Email                string     `json:"email"`
	Name                 string     `json:"name"`
	AgencyRole           string     `json:"agency_role"` // owner ou collaborator
	CreatedAt            time.Time  `json:"created_at"`
	CollaboratorCreatedAt *time.Time `json:"collaborator_created_at,omitempty"`
}

// ListCollaborators lista todos os colaboradores da agência (apenas owner pode ver)
func (h *CollaboratorHandler) ListCollaborators(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	ownerID, _ := r.Context().Value(middleware.UserIDKey).(string)
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	agencyRole, _ := r.Context().Value("agency_role").(string)

	// Apenas owner pode listar colaboradores
	if agencyRole != "owner" {
		log.Printf("❌ COLLABORATOR ACCESS BLOCKED: User %s tried to list collaborators", ownerID)
		http.Error(w, "Only agency owners can manage collaborators", http.StatusForbidden)
		return
	}

	// Buscar todos os usuários da agência
	tenantUUID := parseUUID(tenantID)
	if tenantUUID == nil {
		http.Error(w, "Invalid tenant ID", http.StatusBadRequest)
		return
	}
	users, err := h.userRepo.ListByTenantID(*tenantUUID)
	if err != nil {
		log.Printf("Error fetching collaborators: %v", err)
		http.Error(w, "Error fetching collaborators", http.StatusInternalServerError)
		return
	}

	// Formatar resposta
	collaborators := make([]CollaboratorResponse, 0)
	for _, user := range users {
		collaborators = append(collaborators, CollaboratorResponse{
			ID:                    user.ID.String(),
			Email:                 user.Email,
			Name:                  user.Name,
			AgencyRole:            user.AgencyRole,
			CreatedAt:             user.CreatedAt,
			CollaboratorCreatedAt: user.CollaboratorCreatedAt,
		})
	}

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

// InviteCollaborator convida um novo colaborador para a agência (apenas owner pode fazer isso)
func (h *CollaboratorHandler) InviteCollaborator(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	ownerID, _ := r.Context().Value(middleware.UserIDKey).(string)
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	agencyRole, _ := r.Context().Value("agency_role").(string)

	// Apenas owner pode convidar colaboradores
	if agencyRole != "owner" {
		log.Printf("❌ COLLABORATOR INVITE BLOCKED: User %s tried to invite collaborator", ownerID)
		http.Error(w, "Only agency owners can invite collaborators", http.StatusForbidden)
		return
	}

	var req AddCollaboratorRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	// Validar email
	if req.Email == "" {
		http.Error(w, "Email is required", http.StatusBadRequest)
		return
	}

	// Validar se email já existe
	exists, err := h.userRepo.EmailExists(req.Email)
	if err != nil {
		log.Printf("Error checking email: %v", err)
		http.Error(w, "Error processing request", http.StatusInternalServerError)
		return
	}
	if exists {
		http.Error(w, "Email already registered", http.StatusConflict)
		return
	}

	// Gerar senha temporária (8 caracteres aleatórios)
	tempPassword := generateTempPassword()
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(tempPassword), bcrypt.DefaultCost)
	if err != nil {
		log.Printf("Error hashing password: %v", err)
		http.Error(w, "Error processing request", http.StatusInternalServerError)
		return
	}

	// Criar novo colaborador
	ownerUUID := parseUUID(ownerID)
	tenantUUID := parseUUID(tenantID)
	now := time.Now()

	collaborator := &domain.User{
		TenantID:              tenantUUID,
		Email:                 req.Email,
		Password:              string(hashedPassword),
		Name:                  req.Name,
		Role:                  "ADMIN_AGENCIA",
		AgencyRole:            "collaborator",
		CreatedBy:             ownerUUID,
		CollaboratorCreatedAt: &now,
	}

	if err := h.userRepo.Create(collaborator); err != nil {
		log.Printf("Error creating collaborator: %v", err)
		http.Error(w, "Error creating collaborator", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(map[string]interface{}{
		"message":         "Collaborator invited successfully",
		"temporary_password": tempPassword,
		"collaborator": CollaboratorResponse{
			ID:                    collaborator.ID.String(),
			Email:                 collaborator.Email,
			Name:                  collaborator.Name,
			AgencyRole:            collaborator.AgencyRole,
			CreatedAt:             collaborator.CreatedAt,
			CollaboratorCreatedAt: collaborator.CollaboratorCreatedAt,
		},
	})
}

// RemoveCollaborator remove um colaborador da agência (apenas owner pode fazer isso)
func (h *CollaboratorHandler) RemoveCollaborator(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodDelete {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	ownerID, _ := r.Context().Value(middleware.UserIDKey).(string)
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)
	agencyRole, _ := r.Context().Value("agency_role").(string)

	// Apenas owner pode remover colaboradores
	if agencyRole != "owner" {
		log.Printf("❌ COLLABORATOR REMOVE BLOCKED: User %s tried to remove collaborator", ownerID)
		http.Error(w, "Only agency owners can remove collaborators", http.StatusForbidden)
		return
	}

	collaboratorID := r.URL.Query().Get("id")
	if collaboratorID == "" {
		http.Error(w, "Collaborator ID is required", http.StatusBadRequest)
		return
	}

	// Converter ID para UUID
	collaboratorUUID := parseUUID(collaboratorID)
	if collaboratorUUID == nil {
		http.Error(w, "Invalid collaborator ID", http.StatusBadRequest)
		return
	}

	// Buscar o colaborador
	collaborator, err := h.userRepo.GetByID(*collaboratorUUID)
	if err != nil {
		http.Error(w, "Collaborator not found", http.StatusNotFound)
		return
	}

	// Verificar se o colaborador pertence à mesma agência
	if collaborator.TenantID == nil || collaborator.TenantID.String() != tenantID {
		http.Error(w, "Collaborator not found in this agency", http.StatusForbidden)
		return
	}

	// Não permitir remover o owner
	if collaborator.AgencyRole == "owner" {
		http.Error(w, "Cannot remove the agency owner", http.StatusBadRequest)
		return
	}

	// Remover colaborador
	if err := h.userRepo.Delete(*collaboratorUUID); err != nil {
		log.Printf("Error removing collaborator: %v", err)
		http.Error(w, "Error removing collaborator", http.StatusInternalServerError)
		return
	}

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

// generateTempPassword gera uma senha temporária
func generateTempPassword() string {
	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
	return randomString(12, charset)
}

// randomString gera uma string aleatória
func randomString(length int, charset string) string {
	b := make([]byte, length)
	for i := range b {
		b[i] = charset[i%len(charset)]
	}
	return string(b)
}

// parseUUID converte string para UUID
func parseUUID(s string) *uuid.UUID {
	u, err := uuid.Parse(s)
	if err != nil {
		return nil
	}
	return &u
}
