package codes

import (
	"encoding/csv"
	"io"
	"strconv"
	"time"
)

// CSVRow represents one row in the export CSV.
// The Code field is the only time the plaintext appears outside the
// generation call; it must be streamed directly to the response writer
// or written to an encrypted file and never logged.
type CSVRow struct {
	Index        int
	Code         string   // plaintext activation code (canonical form)
	Plan         PlanCode
	DurationDays int
	BatchID      int64
	Channel      BatchChannel
	GeneratedAt  time.Time
}

// ExportCSV writes the given code rows to w in CSV format.
// Columns: index, code, plan, duration_days, batch_id, channel, generated_at_utc
//
// The caller must ensure that w is the final output destination (HTTP response,
// encrypted file, etc.) and that no intermediate buffer retains the data.
func ExportCSV(w io.Writer, rows []CSVRow) error {
	cw := csv.NewWriter(w)

	// Header row.
	if err := cw.Write([]string{
		"index", "code", "plan", "duration_days",
		"batch_id", "channel", "generated_at_utc",
	}); err != nil {
		return err
	}

	for _, r := range rows {
		rec := []string{
			strconv.Itoa(r.Index),
			r.Code,
			string(r.Plan),
			strconv.Itoa(r.DurationDays),
			strconv.FormatInt(r.BatchID, 10),
			string(r.Channel),
			r.GeneratedAt.UTC().Format(time.RFC3339),
		}
		if err := cw.Write(rec); err != nil {
			return err
		}
	}

	cw.Flush()
	return cw.Error()
}

// BatchResultToCSVRows converts a BatchResult to a slice of CSVRow for export.
// generatedAt should be the time.Now() captured immediately after generation.
func BatchResultToCSVRows(br *BatchResult, generatedAt time.Time) []CSVRow {
	rows := make([]CSVRow, len(br.Codes))
	for i, code := range br.Codes {
		rows[i] = CSVRow{
			Index:        i + 1,
			Code:         code,
			Plan:         br.PlanCode,
			DurationDays: br.DurationDays,
			BatchID:      br.BatchID,
			Channel:      br.Channel,
			GeneratedAt:  generatedAt,
		}
	}
	return rows
}
