package csv

import (
	"encoding/csv"
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"strings"
)

func WriteCSVtoHTTP(w http.ResponseWriter, csv string, filename string) {
	w.Header().Set("Content-Type", "text/csv")
	w.Header().Set("Content-Disposition", "attachment; filename="+filename+".csv")

	w.Write([]byte(csv))
}

func MakeCSV(headers []string, records [][]string) (string, error) {
	if len(records) > 0 {
		if len(headers) != len(records[0]) {
			return "", errors.New("data contains more columns then specified by headers")
		}
	}

	// Write to CSV
	var csvBuilder strings.Builder
	writer := csv.NewWriter(&csvBuilder)

	// Write headers
	if err := writer.Write(headers); err != nil {
		return "", fmt.Errorf("failed to write headers: %v", err)
	}

	// Write records
	for _, record := range records {
		if err := writer.Write(record); err != nil {
			return "", fmt.Errorf("failed to write record: %v", err)
		}
	}

	writer.Flush()
	if err := writer.Error(); err != nil {
		return "", fmt.Errorf("csv writer error: %v", err)
	}

	return csvBuilder.String(), nil
}

func StructToCSV(data any) (string, error) {
	val := reflect.ValueOf(data)
	if val.Kind() != reflect.Slice {
		return "", fmt.Errorf("input must be a slice of structs")
	}

	if val.Len() == 0 {
		return "", nil
	}

	var headers []string
	var records [][]string

	// Get the first element to determine struct fields
	first := val.Index(0)
	if first.Kind() == reflect.Ptr {
		first = first.Elem()
	}

	if first.Kind() != reflect.Struct {
		return "", fmt.Errorf("slice elements must be structs or pointers to structs")
	}

	// Get field names as headers
	t := first.Type()
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		// Use json tag if present, otherwise use field name
		tag := field.Tag.Get("json")
		if tag != "" && tag != "-" {
			headers = append(headers, tag)
		} else {
			headers = append(headers, field.Name)
		}
	}

	// Convert each struct to a record
	for i := 0; i < val.Len(); i++ {
		item := val.Index(i)
		if item.Kind() == reflect.Ptr {
			item = item.Elem()
		}

		var record []string
		for j := 0; j < item.NumField(); j++ {
			fieldVal := item.Field(j)
			record = append(record, fmt.Sprintf("%v", fieldVal.Interface()))
		}
		records = append(records, record)
	}

	// Write to CSV
	var csvBuilder strings.Builder
	writer := csv.NewWriter(&csvBuilder)

	// Write headers
	if err := writer.Write(headers); err != nil {
		return "", fmt.Errorf("failed to write headers: %v", err)
	}

	// Write records
	for _, record := range records {
		if err := writer.Write(record); err != nil {
			return "", fmt.Errorf("failed to write record: %v", err)
		}
	}

	writer.Flush()
	if err := writer.Error(); err != nil {
		return "", fmt.Errorf("csv writer error: %v", err)
	}

	return csvBuilder.String(), nil
}
