package main

import (
	"flag"
	"fmt"
	"io"

	"textmachine/backend/internal/pipeline"
)

// invocation.go: разбор аргументов CLI, выделенный в чистую функцию (пакет №4 —
// у tmctl было 0 тестов на контрактные инварианты: bad-flag→exit 1, а не 2;
// селектор redrive; порядок валидации). Контракт флагов/текстов ошибок ЗАМОРОЖЕН
// (D12/D15.3) — эта функция только переносит его в тестируемое место.

// invocation is one parsed tmctl command line.
type invocation struct {
	cmd        string
	cfgPath    string
	resnapshot bool
	asJSON     bool
	sel        pipeline.RedriveSelector
}

// parseInvocation parses os.Args[1:] into an invocation. flagOut receives the
// stdlib's flag diagnostics ("flag provided but not defined" + usage) — main
// passes os.Stderr, tests a buffer; the bytes and their destination are part of
// the frozen contract.
//
// Deliberately preserved quirks (менять = менять контракт):
//   - the command NAME is not validated here — `tmctl bogus --config x` reaches
//     the dispatch switch (after dotenv/ctx setup) and errors there, while
//     `tmctl bogus` without --config errors "--config book.yaml is required";
//   - ContinueOnError, НЕ ExitOnError: стдлибовский ExitOnError звал бы
//     os.Exit(2) на плохом флаге и КОЛЛАПСИРОВАЛ бы с exit-кодом 2
//     (completed-with-flags); возврат ошибки ведёт плохой флаг в default-ветку
//     main() → exit 1, оставляя 2 эксклюзивным для флагнутых чанков.
func parseInvocation(args []string, flagOut io.Writer) (invocation, error) {
	if len(args) < 1 {
		return invocation{}, fmt.Errorf("usage: tmctl <translate|report|status|redrive> --config book.yaml")
	}
	cmd, rest := args[0], args[1:]

	fs := flag.NewFlagSet(cmd, flag.ContinueOnError)
	fs.SetOutput(flagOut)
	cfgPath := fs.String("config", "", "path to book.yaml")
	resnapshot := fs.Bool("resnapshot", false, "re-pin existing jobs to the current config snapshot (пере-перевод оплаченных чанков — явное согласие)")
	asJSON := fs.Bool("json", false, "status: emit the projection as JSON (stable disposition/flag_reason enums) for CI/IDE")
	chapter := fs.Int("chapter", -1, "redrive: restrict to this chapter (default: any)")
	chunk := fs.Int("chunk", -1, "redrive: restrict to this chunk index within the chapter (default: any)")
	reason := fs.String("reason", "", "redrive: restrict to this flag_reason (default: any)")
	dryRun := fs.Bool("dry-run", false, "redrive: report what would be re-attacked without touching anything")
	if err := fs.Parse(rest); err != nil {
		return invocation{}, err
	}
	if *cfgPath == "" {
		return invocation{}, fmt.Errorf("--config book.yaml is required")
	}
	return invocation{
		cmd: cmd, cfgPath: *cfgPath, resnapshot: *resnapshot, asJSON: *asJSON,
		sel: pipeline.RedriveSelector{
			Chapter: *chapter, ChunkIdx: *chunk, Reason: *reason, DryRun: *dryRun,
		},
	}, nil
}
