package handlers

import (
	"aggios-app/backend/internal/domain"
	"aggios-app/backend/internal/repository"
	"aggios-app/backend/internal/service"
	"aggios-app/backend/internal/config"
	"aggios-app/backend/internal/api/middleware"
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"path/filepath"
	"strings"
	"time"

	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
	"golang.org/x/crypto/bcrypt"
)

type CustomerPortalHandler struct {
	crmRepo     *repository.CRMRepository
	authService *service.AuthService
	cfg         *config.Config
	minioClient *minio.Client
}

func NewCustomerPortalHandler(crmRepo *repository.CRMRepository, authService *service.AuthService, cfg *config.Config) *CustomerPortalHandler {
	// Initialize MinIO client
	minioClient, err := minio.New(cfg.Minio.Endpoint, &minio.Options{
		Creds:  credentials.NewStaticV4(cfg.Minio.RootUser, cfg.Minio.RootPassword, ""),
		Secure: cfg.Minio.UseSSL,
	})
	if err != nil {
		log.Printf("❌ Failed to create MinIO client for CustomerPortalHandler: %v", err)
	}

	return &CustomerPortalHandler{
		crmRepo:     crmRepo,
		authService: authService,
		cfg:         cfg,
		minioClient: minioClient,
	}
}

// CustomerLoginRequest representa a requisição de login do cliente
type CustomerLoginRequest struct {
	Email    string `json:"email"`
	Password string `json:"password"`
}

// CustomerLoginResponse representa a resposta de login do cliente
type CustomerLoginResponse struct {
	Token    string                `json:"token"`
	Customer *CustomerPortalInfo   `json:"customer"`
}

// CustomerPortalInfo representa informações seguras do cliente para o portal
type CustomerPortalInfo struct {
	ID              string    `json:"id"`
	Name            string    `json:"name"`
	Email           string    `json:"email"`
	Company         string    `json:"company"`
	HasPortalAccess bool      `json:"has_portal_access"`
	TenantID        string    `json:"tenant_id"`
}

// Login autentica um cliente e retorna um token JWT
func (h *CustomerPortalHandler) Login(w http.ResponseWriter, r *http.Request) {
	var req CustomerLoginRequest
	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
	}

	// Validar entrada
	if req.Email == "" || req.Password == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Email e senha são obrigatórios",
		})
		return
	}

	// Buscar cliente por email
	customer, err := h.crmRepo.GetCustomerByEmail(req.Email)
	if err != nil {
		if err == sql.ErrNoRows {
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(http.StatusUnauthorized)
			json.NewEncoder(w).Encode(map[string]string{
				"error": "Credenciais inválidas",
			})
			return
		}
		log.Printf("Error fetching customer: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Erro ao processar login",
		})
		return
	}

	// Verificar se tem acesso ao portal
	if !customer.HasPortalAccess {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusForbidden)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Acesso ao portal não autorizado. Entre em contato com o administrador.",
		})
		return
	}

	// Verificar senha
	if err := bcrypt.CompareHashAndPassword([]byte(customer.PasswordHash), []byte(req.Password)); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusUnauthorized)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Credenciais inválidas",
		})
		return
	}

	// Atualizar último login
	if err := h.crmRepo.UpdateCustomerLastLogin(customer.ID); err != nil {
		log.Printf("Warning: Failed to update last login for customer %s: %v", customer.ID, err)
	}

	// Gerar token JWT
	token, err := h.authService.GenerateCustomerToken(customer.ID, customer.TenantID, customer.Email)
	if err != nil {
		log.Printf("Error generating token: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Erro ao gerar token de autenticação",
		})
		return
	}

	// Resposta de sucesso
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(CustomerLoginResponse{
		Token: token,
		Customer: &CustomerPortalInfo{
			ID:              customer.ID,
			Name:            customer.Name,
			Email:           customer.Email,
			Company:         customer.Company,
			HasPortalAccess: customer.HasPortalAccess,
			TenantID:        customer.TenantID,
		},
	})
}

// GetPortalDashboard retorna dados do dashboard para o cliente autenticado
func (h *CustomerPortalHandler) GetPortalDashboard(w http.ResponseWriter, r *http.Request) {
	customerID, _ := r.Context().Value(middleware.CustomerIDKey).(string)
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)

	// Buscar leads do cliente
	leads, err := h.crmRepo.GetLeadsByCustomerID(customerID)
	if err != nil {
		log.Printf("Error fetching leads: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Erro ao buscar leads",
		})
		return
	}

	// Buscar informações do cliente
	customer, err := h.crmRepo.GetCustomerByID(customerID, tenantID)
	if err != nil {
		log.Printf("Error fetching customer: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Erro ao buscar informações do cliente",
		})
		return
	}

	// Calcular estatísticas
	rawStats := calculateLeadStats(leads)
	stats := map[string]interface{}{
		"total_leads":  rawStats["total"],
		"active_leads": rawStats["novo"].(int) + rawStats["qualificado"].(int) + rawStats["negociacao"].(int),
		"converted":    rawStats["convertido"],
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"customer": CustomerPortalInfo{
			ID:              customer.ID,
			Name:            customer.Name,
			Email:           customer.Email,
			Company:         customer.Company,
			HasPortalAccess: customer.HasPortalAccess,
			TenantID:        customer.TenantID,
		},
		"leads": leads,
		"stats": stats,
	})
}

// GetPortalLeads retorna apenas os leads do cliente
func (h *CustomerPortalHandler) GetPortalLeads(w http.ResponseWriter, r *http.Request) {
	customerID, _ := r.Context().Value(middleware.CustomerIDKey).(string)

	leads, err := h.crmRepo.GetLeadsByCustomerID(customerID)
	if err != nil {
		log.Printf("Error fetching leads: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Erro ao buscar leads",
		})
		return
	}

	if leads == nil {
		leads = []domain.CRMLead{}
	}

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

// GetPortalLists retorna as listas que possuem leads do cliente
func (h *CustomerPortalHandler) GetPortalLists(w http.ResponseWriter, r *http.Request) {
	customerID, _ := r.Context().Value(middleware.CustomerIDKey).(string)

	lists, err := h.crmRepo.GetListsByCustomerID(customerID)
	if err != nil {
		log.Printf("Error fetching portal lists: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Erro ao buscar listas",
		})
		return
	}

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

// GetPortalProfile retorna o perfil completo do cliente
func (h *CustomerPortalHandler) GetPortalProfile(w http.ResponseWriter, r *http.Request) {
	customerID, _ := r.Context().Value(middleware.CustomerIDKey).(string)
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)

	// Buscar informações do cliente
	customer, err := h.crmRepo.GetCustomerByID(customerID, tenantID)
	if err != nil {
		log.Printf("Error fetching customer: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Erro ao buscar perfil",
		})
		return
	}

	// Buscar leads para estatísticas
	leads, err := h.crmRepo.GetLeadsByCustomerID(customerID)
	if err != nil {
		log.Printf("Error fetching leads for stats: %v", err)
		leads = []domain.CRMLead{}
	}

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

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"customer": map[string]interface{}{
			"id":                customer.ID,
			"name":              customer.Name,
			"email":             customer.Email,
			"phone":             customer.Phone,
			"company":           customer.Company,
			"logo_url":          customer.LogoURL,
			"portal_last_login": customer.PortalLastLogin,
			"created_at":        customer.CreatedAt,
			"total_leads":       len(leads),
			"converted_leads":   stats["convertido"].(int),
		},
	})
}

// ChangePasswordRequest representa a requisição de troca de senha
type CustomerChangePasswordRequest struct {
	CurrentPassword string `json:"current_password"`
	NewPassword     string `json:"new_password"`
}

// ChangePassword altera a senha do cliente
func (h *CustomerPortalHandler) ChangePassword(w http.ResponseWriter, r *http.Request) {
	customerID, _ := r.Context().Value(middleware.CustomerIDKey).(string)
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)

	var req CustomerChangePasswordRequest
	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
	}

	// Validar entrada
	if req.CurrentPassword == "" || req.NewPassword == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Senha atual e nova senha são obrigatórias",
		})
		return
	}

	if len(req.NewPassword) < 6 {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusBadRequest)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "A nova senha deve ter no mínimo 6 caracteres",
		})
		return
	}

	// Buscar cliente
	customer, err := h.crmRepo.GetCustomerByID(customerID, tenantID)
	if err != nil {
		log.Printf("Error fetching customer: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Erro ao processar solicitação",
		})
		return
	}

	// Verificar senha atual
	if err := bcrypt.CompareHashAndPassword([]byte(customer.PasswordHash), []byte(req.CurrentPassword)); err != nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusUnauthorized)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Senha atual incorreta",
		})
		return
	}

	// Gerar hash da nova senha
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), 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": "Erro ao processar nova senha",
		})
		return
	}

	// Atualizar senha no banco
	if err := h.crmRepo.UpdateCustomerPassword(customerID, string(hashedPassword)); err != nil {
		log.Printf("Error updating password: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		json.NewEncoder(w).Encode(map[string]string{
			"error": "Erro ao atualizar senha",
		})
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{
		"message": "Senha alterada com sucesso",
	})
}

// UploadLogo faz o upload do logo do cliente
func (h *CustomerPortalHandler) UploadLogo(w http.ResponseWriter, r *http.Request) {
	customerID, _ := r.Context().Value(middleware.CustomerIDKey).(string)
	tenantID, _ := r.Context().Value(middleware.TenantIDKey).(string)

	if h.minioClient == nil {
		http.Error(w, "Storage service unavailable", http.StatusServiceUnavailable)
		return
	}

	// Parse multipart form (2MB max)
	const maxLogoSize = 2 * 1024 * 1024
	if err := r.ParseMultipartForm(maxLogoSize); err != nil {
		http.Error(w, "File too large", http.StatusBadRequest)
		return
	}

	file, header, err := r.FormFile("logo")
	if err != nil {
		http.Error(w, "Failed to read file", http.StatusBadRequest)
		return
	}
	defer file.Close()

	// Validate file type
	contentType := header.Header.Get("Content-Type")
	if !strings.HasPrefix(contentType, "image/") {
		http.Error(w, "Only images are allowed", http.StatusBadRequest)
		return
	}

	// Generate unique filename
	ext := filepath.Ext(header.Filename)
	if ext == "" {
		ext = ".png" // Default extension
	}
	filename := fmt.Sprintf("logo-%d%s", time.Now().Unix(), ext)
	objectPath := fmt.Sprintf("customers/%s/%s", customerID, filename)

	// Upload to MinIO
	ctx := context.Background()
	bucketName := h.cfg.Minio.BucketName
	
	_, err = h.minioClient.PutObject(ctx, bucketName, objectPath, file, header.Size, minio.PutObjectOptions{
		ContentType: contentType,
	})
	if err != nil {
		log.Printf("Error uploading to MinIO: %v", err)
		http.Error(w, "Failed to upload file", http.StatusInternalServerError)
		return
	}

	// Generate public URL
	logoURL := fmt.Sprintf("%s/api/files/%s/%s", h.cfg.Minio.PublicURL, bucketName, objectPath)

	// Update customer in database
	err = h.crmRepo.UpdateCustomerLogo(customerID, tenantID, logoURL)
	if err != nil {
		log.Printf("Error updating customer logo in DB: %v", err)
		http.Error(w, "Failed to update profile", http.StatusInternalServerError)
		return
	}

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