package csv

import (
	"context"
	"encoding/csv"
	"io"
	"strings"
)

type RowHandler func(row []string, rowNumber int) error

func ReadCSV(file io.Reader, handler RowHandler) (int, []string, error) {
	return ReadCSVWithContext(context.Background(), file, handler)
}

func ReadCSVWithContext(ctx context.Context, file io.Reader, handler RowHandler) (int, []string, error) {
	reader := csv.NewReader(file)
	reader.LazyQuotes = true
	reader.TrimLeadingSpace = true

	var errors []string
	rowNumber := 0
	totalRows := 0

	for {
		select {
		case <-ctx.Done():
			return totalRows, errors, ctx.Err()
		default:
		}

		row, err := reader.Read()
		if err == io.EOF {
			break
		}
		rowNumber++

		if err != nil {
			errors = append(errors, "Row "+itoa(rowNumber)+": "+err.Error())
			continue
		}

		if len(row) == 0 || (len(row) == 1 && strings.TrimSpace(row[0]) == "") {
			continue
		}

		if rowNumber == 1 {
			continue
		}

		if err := handler(row, rowNumber); err != nil {
			if ctx.Err() != nil {
				return totalRows, errors, ctx.Err()
			}
			errors = append(errors, "Row "+itoa(rowNumber)+": "+err.Error())
		}
		totalRows++
	}

	return totalRows, errors, nil
}

func CountRows(file io.Reader) (int, error) {
	reader := csv.NewReader(file)
	count := 0
	for {
		_, err := reader.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			continue
		}
		count++
	}
	return count, nil
}

func WriteCSV(headers []string, rows [][]string) string {
	var sb strings.Builder
	writer := csv.NewWriter(&sb)

	writer.Write(headers)
	for _, row := range rows {
		writer.Write(row)
	}
	writer.Flush()

	return sb.String()
}

func itoa(i int) string {
	if i == 0 {
		return "0"
	}
	result := ""
	for i > 0 {
		result = string(rune('0'+i%10)) + result
		i /= 10
	}
	return result
}
