// Command codegen is a CLI transition tool for batch-generating activation codes
// before the admin panel (task #8) is ready.
//
// Usage:
//
//	codegen -plan=pro -days=30 -count=100 -channel=manual \
//	        -note="telegram batch june" -dsn="root:pass@tcp(127.0.0.1:3306)/pangolin?parseTime=true" \
//	        [-out=codes.csv]
//
// The generated plaintext codes are written to stdout (or -out) as a CSV file.
// They are NEVER logged or stored in the database.
// The database receives only the SHA-256 hashes.
//
// Run `codegen -help` for full flag documentation.
package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/wangjia/pangolin/server/internal/codes"
	"github.com/wangjia/pangolin/server/internal/db"
)

func main() {
	plan := flag.String("plan", "", "Plan code: free | pro | team  (required)")
	days := flag.Int("days", 0, "Duration in days the code grants  (required, >0)")
	count := flag.Int("count", 0, "Number of codes to generate  (required, >0)")
	channel := flag.String("channel", "manual", "Distribution channel: store | tg | line | manual")
	note := flag.String("note", "", "Human-readable batch note (optional)")
	dsn := flag.String("dsn", "", "MySQL DSN, e.g. user:pass@tcp(host:port)/dbname?parseTime=true  (required, or set DB_DSN env)")
	out := flag.String("out", "", "Output CSV file path (default: stdout)")
	createdBy := flag.String("by", "cli", "Identifier of the operator creating this batch")
	flag.Parse()

	// Resolve DSN from flag or environment.
	dsnVal := *dsn
	if dsnVal == "" {
		dsnVal = os.Getenv("DB_DSN")
	}

	// Validation.
	if dsnVal == "" {
		log.Fatal("codegen: -dsn or DB_DSN is required")
	}
	if *plan == "" {
		log.Fatal("codegen: -plan is required")
	}
	if *days <= 0 {
		log.Fatal("codegen: -days must be > 0")
	}
	if *count <= 0 {
		log.Fatal("codegen: -count must be > 0")
	}

	planCode := codes.PlanCode(*plan)
	switch planCode {
	case codes.PlanFree, codes.PlanPro, codes.PlanTeam:
	default:
		log.Fatalf("codegen: unknown plan %q; must be free | pro | team", *plan)
	}

	ch := codes.BatchChannel(*channel)
	switch ch {
	case codes.ChannelStore, codes.ChannelTG, codes.ChannelLine, codes.ChannelManual:
	default:
		log.Fatalf("codegen: unknown channel %q; must be store | tg | line | manual", *channel)
	}

	// Open database.
	dbConn, err := db.Open(dsnVal)
	if err != nil {
		log.Fatalf("codegen: database: %v", err)
	}
	defer dbConn.Close()

	store := codes.NewStore(dbConn)
	// Service is created without Redis (batch generation doesn't need rate limiting).
	svc := codes.NewService(store, nil, 5, time.Hour)

	log.Printf("codegen: generating %d %s/%dd codes for channel=%s …", *count, planCode, *days, ch)

	result, err := svc.CreateBatch(context.Background(), codes.BatchRequest{
		PlanCode:     planCode,
		DurationDays: *days,
		Count:        *count,
		Channel:      ch,
		Note:         *note,
		CreatedBy:    *createdBy,
	})
	if err != nil {
		log.Fatalf("codegen: CreateBatch: %v", err)
	}

	log.Printf("codegen: batch %d created; writing CSV …", result.BatchID)

	// Determine output writer.
	output := os.Stdout
	if *out != "" {
		f, err := os.Create(*out)
		if err != nil {
			log.Fatalf("codegen: open output file: %v", err)
		}
		defer f.Close()
		output = f
	}

	csvRows := make([]codes.CSVRow, len(result.Codes))
	now := time.Now().UTC()
	for i, c := range result.Codes {
		csvRows[i] = codes.CSVRow{
			Index:        i + 1,
			Code:         c,
			Plan:         planCode,
			DurationDays: *days,
			BatchID:      result.BatchID,
			Channel:      ch,
			GeneratedAt:  now,
		}
	}

	if err := codes.ExportCSV(output, csvRows); err != nil {
		log.Fatalf("codegen: ExportCSV: %v", err)
	}

	if *out != "" {
		fmt.Printf("codegen: %d codes written to %s\n", *count, *out)
		fmt.Printf("codegen: ⚠️  Treat this file as a secret — it contains plaintext activation codes.\n")
	} else {
		fmt.Fprintf(os.Stderr, "codegen: %d codes written to stdout\n", *count)
		fmt.Fprintf(os.Stderr, "codegen: ⚠️  Treat the output as a secret — it contains plaintext activation codes.\n")
	}
}
