package handlers

import (
	"bytes"
	"fmt"
	"net/http"
	"time"

	"github.com/breakpilot/ai-compliance-sdk/internal/dsgvo"
	"github.com/breakpilot/ai-compliance-sdk/internal/rbac"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

// DSGVOHandlers handles DSGVO-related API endpoints
type DSGVOHandlers struct {
	store *dsgvo.Store
}

// NewDSGVOHandlers creates new DSGVO handlers
func NewDSGVOHandlers(store *dsgvo.Store) *DSGVOHandlers {
	return &DSGVOHandlers{store: store}
}

// ============================================================================
// VVT - Verarbeitungsverzeichnis (Processing Activities)
// ============================================================================

// ListProcessingActivities returns all processing activities for a tenant
func (h *DSGVOHandlers) ListProcessingActivities(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	var namespaceID *uuid.UUID
	if nsID := c.Query("namespace_id"); nsID != "" {
		if id, err := uuid.Parse(nsID); err == nil {
			namespaceID = &id
		}
	}

	activities, err := h.store.ListProcessingActivities(c.Request.Context(), tenantID, namespaceID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"processing_activities": activities})
}

// GetProcessingActivity returns a processing activity by ID
func (h *DSGVOHandlers) GetProcessingActivity(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
		return
	}

	activity, err := h.store.GetProcessingActivity(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if activity == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
		return
	}

	c.JSON(http.StatusOK, activity)
}

// CreateProcessingActivity creates a new processing activity
func (h *DSGVOHandlers) CreateProcessingActivity(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	userID := rbac.GetUserID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	var activity dsgvo.ProcessingActivity
	if err := c.ShouldBindJSON(&activity); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	activity.TenantID = tenantID
	activity.CreatedBy = userID
	if activity.Status == "" {
		activity.Status = "draft"
	}

	if err := h.store.CreateProcessingActivity(c.Request.Context(), &activity); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, activity)
}

// UpdateProcessingActivity updates a processing activity
func (h *DSGVOHandlers) UpdateProcessingActivity(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
		return
	}

	var activity dsgvo.ProcessingActivity
	if err := c.ShouldBindJSON(&activity); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	activity.ID = id
	if err := h.store.UpdateProcessingActivity(c.Request.Context(), &activity); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, activity)
}

// DeleteProcessingActivity deletes a processing activity
func (h *DSGVOHandlers) DeleteProcessingActivity(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
		return
	}

	if err := h.store.DeleteProcessingActivity(c.Request.Context(), id); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "deleted"})
}

// ============================================================================
// TOM - Technische und Organisatorische Maßnahmen
// ============================================================================

// ListTOMs returns all TOMs for a tenant
func (h *DSGVOHandlers) ListTOMs(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	category := c.Query("category")

	toms, err := h.store.ListTOMs(c.Request.Context(), tenantID, category)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"toms": toms, "categories": dsgvo.TOMCategories})
}

// GetTOM returns a TOM by ID
func (h *DSGVOHandlers) GetTOM(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
		return
	}

	tom, err := h.store.GetTOM(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if tom == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
		return
	}

	c.JSON(http.StatusOK, tom)
}

// CreateTOM creates a new TOM
func (h *DSGVOHandlers) CreateTOM(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	userID := rbac.GetUserID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	var tom dsgvo.TOM
	if err := c.ShouldBindJSON(&tom); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	tom.TenantID = tenantID
	tom.CreatedBy = userID
	if tom.ImplementationStatus == "" {
		tom.ImplementationStatus = "planned"
	}

	if err := h.store.CreateTOM(c.Request.Context(), &tom); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, tom)
}

// ============================================================================
// DSR - Data Subject Requests
// ============================================================================

// ListDSRs returns all DSRs for a tenant
func (h *DSGVOHandlers) ListDSRs(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	status := c.Query("status")
	requestType := c.Query("type")

	dsrs, err := h.store.ListDSRs(c.Request.Context(), tenantID, status, requestType)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"dsrs": dsrs, "types": dsgvo.DSRTypes})
}

// GetDSR returns a DSR by ID
func (h *DSGVOHandlers) GetDSR(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
		return
	}

	dsr, err := h.store.GetDSR(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if dsr == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
		return
	}

	c.JSON(http.StatusOK, dsr)
}

// CreateDSR creates a new DSR
func (h *DSGVOHandlers) CreateDSR(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	userID := rbac.GetUserID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	var dsr dsgvo.DSR
	if err := c.ShouldBindJSON(&dsr); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	dsr.TenantID = tenantID
	dsr.CreatedBy = userID
	if dsr.Status == "" {
		dsr.Status = "received"
	}

	if err := h.store.CreateDSR(c.Request.Context(), &dsr); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, dsr)
}

// UpdateDSR updates a DSR
func (h *DSGVOHandlers) UpdateDSR(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
		return
	}

	var dsr dsgvo.DSR
	if err := c.ShouldBindJSON(&dsr); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	dsr.ID = id
	if err := h.store.UpdateDSR(c.Request.Context(), &dsr); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, dsr)
}

// ============================================================================
// Retention Policies
// ============================================================================

// ListRetentionPolicies returns all retention policies for a tenant
func (h *DSGVOHandlers) ListRetentionPolicies(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	policies, err := h.store.ListRetentionPolicies(c.Request.Context(), tenantID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"policies":        policies,
		"common_periods":  dsgvo.CommonRetentionPeriods,
	})
}

// CreateRetentionPolicy creates a new retention policy
func (h *DSGVOHandlers) CreateRetentionPolicy(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	userID := rbac.GetUserID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	var policy dsgvo.RetentionPolicy
	if err := c.ShouldBindJSON(&policy); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	policy.TenantID = tenantID
	policy.CreatedBy = userID
	if policy.Status == "" {
		policy.Status = "draft"
	}

	if err := h.store.CreateRetentionPolicy(c.Request.Context(), &policy); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, policy)
}

// ============================================================================
// Statistics
// ============================================================================

// GetStats returns DSGVO statistics for a tenant
func (h *DSGVOHandlers) GetStats(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	stats, err := h.store.GetStats(c.Request.Context(), tenantID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, stats)
}

// ============================================================================
// DSFA - Datenschutz-Folgenabschätzung
// ============================================================================

// ListDSFAs returns all DSFAs for a tenant
func (h *DSGVOHandlers) ListDSFAs(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	status := c.Query("status")

	dsfas, err := h.store.ListDSFAs(c.Request.Context(), tenantID, status)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"dsfas": dsfas})
}

// GetDSFA returns a DSFA by ID
func (h *DSGVOHandlers) GetDSFA(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
		return
	}

	dsfa, err := h.store.GetDSFA(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if dsfa == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
		return
	}

	c.JSON(http.StatusOK, dsfa)
}

// CreateDSFA creates a new DSFA
func (h *DSGVOHandlers) CreateDSFA(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	userID := rbac.GetUserID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	var dsfa dsgvo.DSFA
	if err := c.ShouldBindJSON(&dsfa); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	dsfa.TenantID = tenantID
	dsfa.CreatedBy = userID
	if dsfa.Status == "" {
		dsfa.Status = "draft"
	}

	if err := h.store.CreateDSFA(c.Request.Context(), &dsfa); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, dsfa)
}

// UpdateDSFA updates a DSFA
func (h *DSGVOHandlers) UpdateDSFA(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
		return
	}

	var dsfa dsgvo.DSFA
	if err := c.ShouldBindJSON(&dsfa); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	dsfa.ID = id
	if err := h.store.UpdateDSFA(c.Request.Context(), &dsfa); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, dsfa)
}

// DeleteDSFA deletes a DSFA
func (h *DSGVOHandlers) DeleteDSFA(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
		return
	}

	if err := h.store.DeleteDSFA(c.Request.Context(), id); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "deleted"})
}

// ============================================================================
// PDF Export
// ============================================================================

// ExportVVT exports the Verarbeitungsverzeichnis as CSV/JSON
func (h *DSGVOHandlers) ExportVVT(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	format := c.DefaultQuery("format", "csv")

	activities, err := h.store.ListProcessingActivities(c.Request.Context(), tenantID, nil)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	if format == "json" {
		c.Header("Content-Disposition", "attachment; filename=vvt_export.json")
		c.JSON(http.StatusOK, gin.H{
			"exported_at":           time.Now().UTC().Format(time.RFC3339),
			"processing_activities": activities,
		})
		return
	}

	// CSV Export
	var buf bytes.Buffer
	buf.WriteString("ID;Name;Zweck;Rechtsgrundlage;Datenkategorien;Betroffene;Empfänger;Drittland;Aufbewahrung;Verantwortlich;Status;Erstellt\n")

	for _, pa := range activities {
		buf.WriteString(fmt.Sprintf("%s;%s;%s;%s;%s;%s;%s;%t;%s;%s;%s;%s\n",
			pa.ID.String(),
			escapeCSV(pa.Name),
			escapeCSV(pa.Purpose),
			pa.LegalBasis,
			joinStrings(pa.DataCategories),
			joinStrings(pa.DataSubjectCategories),
			joinStrings(pa.Recipients),
			pa.ThirdCountryTransfer,
			pa.RetentionPeriod,
			escapeCSV(pa.ResponsiblePerson),
			pa.Status,
			pa.CreatedAt.Format("2006-01-02"),
		))
	}

	c.Header("Content-Type", "text/csv; charset=utf-8")
	c.Header("Content-Disposition", "attachment; filename=vvt_export.csv")
	c.Data(http.StatusOK, "text/csv; charset=utf-8", buf.Bytes())
}

// ExportTOM exports the TOM catalog as CSV/JSON
func (h *DSGVOHandlers) ExportTOM(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	format := c.DefaultQuery("format", "csv")

	toms, err := h.store.ListTOMs(c.Request.Context(), tenantID, "")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	if format == "json" {
		c.Header("Content-Disposition", "attachment; filename=tom_export.json")
		c.JSON(http.StatusOK, gin.H{
			"exported_at": time.Now().UTC().Format(time.RFC3339),
			"toms":        toms,
			"categories":  dsgvo.TOMCategories,
		})
		return
	}

	// CSV Export
	var buf bytes.Buffer
	buf.WriteString("ID;Kategorie;Name;Beschreibung;Typ;Status;Implementiert am;Verantwortlich;Wirksamkeit;Erstellt\n")

	for _, tom := range toms {
		implementedAt := ""
		if tom.ImplementedAt != nil {
			implementedAt = tom.ImplementedAt.Format("2006-01-02")
		}
		buf.WriteString(fmt.Sprintf("%s;%s;%s;%s;%s;%s;%s;%s;%s;%s\n",
			tom.ID.String(),
			tom.Category,
			escapeCSV(tom.Name),
			escapeCSV(tom.Description),
			tom.Type,
			tom.ImplementationStatus,
			implementedAt,
			escapeCSV(tom.ResponsiblePerson),
			tom.EffectivenessRating,
			tom.CreatedAt.Format("2006-01-02"),
		))
	}

	c.Header("Content-Type", "text/csv; charset=utf-8")
	c.Header("Content-Disposition", "attachment; filename=tom_export.csv")
	c.Data(http.StatusOK, "text/csv; charset=utf-8", buf.Bytes())
}

// ExportDSR exports DSR overview as CSV/JSON
func (h *DSGVOHandlers) ExportDSR(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	format := c.DefaultQuery("format", "csv")

	dsrs, err := h.store.ListDSRs(c.Request.Context(), tenantID, "", "")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	if format == "json" {
		c.Header("Content-Disposition", "attachment; filename=dsr_export.json")
		c.JSON(http.StatusOK, gin.H{
			"exported_at": time.Now().UTC().Format(time.RFC3339),
			"dsrs":        dsrs,
			"types":       dsgvo.DSRTypes,
		})
		return
	}

	// CSV Export
	var buf bytes.Buffer
	buf.WriteString("ID;Typ;Name;E-Mail;Status;Eingegangen;Frist;Abgeschlossen;Kanal;Zugewiesen\n")

	for _, dsr := range dsrs {
		completedAt := ""
		if dsr.CompletedAt != nil {
			completedAt = dsr.CompletedAt.Format("2006-01-02")
		}
		assignedTo := ""
		if dsr.AssignedTo != nil {
			assignedTo = dsr.AssignedTo.String()
		}
		buf.WriteString(fmt.Sprintf("%s;%s;%s;%s;%s;%s;%s;%s;%s;%s\n",
			dsr.ID.String(),
			dsr.RequestType,
			escapeCSV(dsr.SubjectName),
			dsr.SubjectEmail,
			dsr.Status,
			dsr.ReceivedAt.Format("2006-01-02"),
			dsr.DeadlineAt.Format("2006-01-02"),
			completedAt,
			dsr.RequestChannel,
			assignedTo,
		))
	}

	c.Header("Content-Type", "text/csv; charset=utf-8")
	c.Header("Content-Disposition", "attachment; filename=dsr_export.csv")
	c.Data(http.StatusOK, "text/csv; charset=utf-8", buf.Bytes())
}

// ExportDSFA exports a DSFA as JSON
func (h *DSGVOHandlers) ExportDSFA(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
		return
	}

	dsfa, err := h.store.GetDSFA(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if dsfa == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
		return
	}

	c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=dsfa_%s.json", id.String()[:8]))
	c.JSON(http.StatusOK, gin.H{
		"exported_at": time.Now().UTC().Format(time.RFC3339),
		"dsfa":        dsfa,
	})
}

// ExportRetentionPolicies exports retention policies as CSV/JSON
func (h *DSGVOHandlers) ExportRetentionPolicies(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	format := c.DefaultQuery("format", "csv")

	policies, err := h.store.ListRetentionPolicies(c.Request.Context(), tenantID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	if format == "json" {
		c.Header("Content-Disposition", "attachment; filename=retention_policies_export.json")
		c.JSON(http.StatusOK, gin.H{
			"exported_at":      time.Now().UTC().Format(time.RFC3339),
			"policies":         policies,
			"common_periods":   dsgvo.CommonRetentionPeriods,
		})
		return
	}

	// CSV Export
	var buf bytes.Buffer
	buf.WriteString("ID;Name;Datenkategorie;Aufbewahrungsdauer (Tage);Dauer (Text);Rechtsgrundlage;Referenz;Löschmethode;Status\n")

	for _, rp := range policies {
		buf.WriteString(fmt.Sprintf("%s;%s;%s;%d;%s;%s;%s;%s;%s\n",
			rp.ID.String(),
			escapeCSV(rp.Name),
			rp.DataCategory,
			rp.RetentionPeriodDays,
			escapeCSV(rp.RetentionPeriodText),
			escapeCSV(rp.LegalBasis),
			escapeCSV(rp.LegalReference),
			rp.DeletionMethod,
			rp.Status,
		))
	}

	c.Header("Content-Type", "text/csv; charset=utf-8")
	c.Header("Content-Disposition", "attachment; filename=retention_policies_export.csv")
	c.Data(http.StatusOK, "text/csv; charset=utf-8", buf.Bytes())
}

// Helper functions

func escapeCSV(s string) string {
	// Simple CSV escaping - wrap in quotes if contains semicolon, quote, or newline
	if s == "" {
		return ""
	}
	needsQuotes := false
	for _, c := range s {
		if c == ';' || c == '"' || c == '\n' || c == '\r' {
			needsQuotes = true
			break
		}
	}
	if needsQuotes {
		// Double any quotes and wrap in quotes
		escaped := ""
		for _, c := range s {
			if c == '"' {
				escaped += "\"\""
			} else if c == '\n' || c == '\r' {
				escaped += " "
			} else {
				escaped += string(c)
			}
		}
		return "\"" + escaped + "\""
	}
	return s
}

func joinStrings(strs []string) string {
	if len(strs) == 0 {
		return ""
	}
	result := strs[0]
	for i := 1; i < len(strs); i++ {
		result += ", " + strs[i]
	}
	return result
}
