package cmd

import (
	"encoding/json"
	"fmt"
	"os"
	"strconv"
	"strings"

	"skraak/tools"
)

func printModifyUsage() {
	fmt.Fprintf(os.Stderr, "Usage: skraak calls modify [options]\n\n")
	fmt.Fprintf(os.Stderr, "Modify a label in a .data file.\n\n")
	fmt.Fprintf(os.Stderr, "Options:\n")
	fmt.Fprintf(os.Stderr, "  --file <path>       Path to .data file (required)\n")
	fmt.Fprintf(os.Stderr, "  --reviewer <name>   Reviewer name (required)\n")
	fmt.Fprintf(os.Stderr, "  --filter <name>     Filter name to match labels (required)\n")
	fmt.Fprintf(os.Stderr, "  --segment <start-end>  Segment time range in integer seconds (required, e.g., 12-15)\n")
	fmt.Fprintf(os.Stderr, "  --certainty <int>   Certainty value 0-100 (required)\n")
	fmt.Fprintf(os.Stderr, "  --species <name>    Species to set (e.g., Kiwi, Kiwi+Male, Noise)\n")
	fmt.Fprintf(os.Stderr, "  --bookmark          Mark segment as bookmarked for navigation\n")
	fmt.Fprintf(os.Stderr, "  --comment <text>    User comment (max 140 chars, ASCII only)\n")
	fmt.Fprintf(os.Stderr, "\nSegment matching:\n")
	fmt.Fprintf(os.Stderr, "  Segments are matched by floor(start) and ceil(end) times.\n")
	fmt.Fprintf(os.Stderr, "  For example, a segment from 12.3s to 14.5s matches --segment 12-15.\n")
	fmt.Fprintf(os.Stderr, "\nBehavior:\n")
	fmt.Fprintf(os.Stderr, "  Always updates reviewer on file metadata.\n")
	fmt.Fprintf(os.Stderr, "  If all specified values match current values, no modification is made.\n")
	fmt.Fprintf(os.Stderr, "\nExamples:\n")
	fmt.Fprintf(os.Stderr, "  # Change species and certainty (incorrect classification)\n")
	fmt.Fprintf(os.Stderr, "  skraak calls modify --file recording.data --reviewer GLM-5 \\\n")
	fmt.Fprintf(os.Stderr, "    --filter mymodel --segment 12-15 --species Kiwi+Male --certainty 100\n\n")
	fmt.Fprintf(os.Stderr, "  # Change certainty only (correct classification)\n")
	fmt.Fprintf(os.Stderr, "  skraak calls modify --file recording.data --reviewer GLM-5 \\\n")
	fmt.Fprintf(os.Stderr, "    --filter mymodel --segment 12-15 --certainty 100\n\n")
	fmt.Fprintf(os.Stderr, "  # Change to Noise (clears calltype)\n")
	fmt.Fprintf(os.Stderr, "  skraak calls modify --file recording.data --reviewer GLM-5 \\\n")
	fmt.Fprintf(os.Stderr, "    --filter mymodel --segment 67-88 --species Noise --certainty 100\n\n")
	fmt.Fprintf(os.Stderr, "  # Bookmark a segment for later review\n")
	fmt.Fprintf(os.Stderr, "  skraak calls modify --file recording.data --reviewer GLM-5 \\\n")
	fmt.Fprintf(os.Stderr, "    --filter mymodel --segment 12-15 --certainty 100 --bookmark\n\n")
	fmt.Fprintf(os.Stderr, "  # Add a comment to a segment\n")
	fmt.Fprintf(os.Stderr, "  skraak calls modify --file recording.data --reviewer GLM-5 \\\n")
	fmt.Fprintf(os.Stderr, "    --filter mymodel --segment 12-15 --certainty 100 --comment \"Good example of duet\"\n")
}

// modifyArgs holds parsed CLI arguments for the modify command.
type modifyArgs struct {
	file         string
	reviewer     string
	filter       string
	segment      string
	species      string
	comment      string
	certainty    int
	certaintySet bool
	bookmark     bool
}

// requireFlagValue extracts the value for a flag that requires an argument.
// Exits on missing value.
func requireFlagValue(args []string, i int, flagName string) string {
	if i+1 >= len(args) {
		fmt.Fprintf(os.Stderr, "Error: %s requires a value\n", flagName)
		os.Exit(1)
	}
	return args[i+1]
}

// parseModifyArgs parses the command-line arguments for the modify subcommand.
func parseModifyArgs(args []string) modifyArgs {
	var ma modifyArgs
	i := 0
	for i < len(args) {
		arg := args[i]
		switch arg {
		case "--file":
			ma.file = requireFlagValue(args, i, "--file")
			i += 2
		case "--reviewer":
			ma.reviewer = requireFlagValue(args, i, "--reviewer")
			i += 2
		case "--filter":
			ma.filter = requireFlagValue(args, i, "--filter")
			i += 2
		case "--segment":
			ma.segment = requireFlagValue(args, i, "--segment")
			i += 2
		case "--species":
			ma.species = requireFlagValue(args, i, "--species")
			i += 2
		case "--certainty":
			val := requireFlagValue(args, i, "--certainty")
			v, err := strconv.Atoi(val)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error: --certainty must be an integer\n")
				os.Exit(1)
			}
			ma.certainty = v
			ma.certaintySet = true
			i += 2
		case "--bookmark":
			ma.bookmark = true
			i++
		case "--comment":
			ma.comment = requireFlagValue(args, i, "--comment")
			i += 2
		case "-h", "--help":
			printModifyUsage()
			os.Exit(0)
		default:
			if strings.HasPrefix(arg, "--") {
				fmt.Fprintf(os.Stderr, "Error: unknown flag: %s\n\n", arg)
				printModifyUsage()
				os.Exit(1)
			}
			i++
		}
	}
	return ma
}

// validateModifyArgs checks required flags and value ranges.
func validateModifyArgs(ma modifyArgs) {
	missing := []string{}
	if ma.file == "" {
		missing = append(missing, "--file")
	}
	if ma.reviewer == "" {
		missing = append(missing, "--reviewer")
	}
	if ma.filter == "" {
		missing = append(missing, "--filter")
	}
	if ma.segment == "" {
		missing = append(missing, "--segment")
	}
	if !ma.certaintySet {
		missing = append(missing, "--certainty")
	}
	if len(missing) > 0 {
		fmt.Fprintf(os.Stderr, "Error: missing required flags: %v\n\n", missing)
		printModifyUsage()
		os.Exit(1)
	}
	if ma.certainty < 0 || ma.certainty > 100 {
		fmt.Fprintf(os.Stderr, "Error: --certainty must be between 0 and 100\n")
		os.Exit(1)
	}
}

// RunCallsModify handles the "calls modify" subcommand
//
// JSON output schema:
//
//	{
//	  "file": string,               // .data file path
//	  "segment_start": int,        // Matched segment start (seconds, floored)
//	  "segment_end": int,          // Matched segment end (seconds, ceiled)
//	  "species": string,           // Updated species (omitted if unchanged)
//	  "calltype": string,          // Updated call type (omitted if empty)
//	  "certainty": int,            // Updated certainty (omitted if unchanged)
//	  "bookmark": bool,            // Bookmark flag (omitted if not set)
//	  "comment": string,           // Comment (omitted if empty)
//	  "previous_value": string,    // Description of previous label value (omitted if unchanged)
//	  "error": string              // Error message (omitted if no error)
//	}
func RunCallsModify(args []string) {
	ma := parseModifyArgs(args)
	validateModifyArgs(ma)

	input := tools.CallsModifyInput{
		File:      ma.file,
		Reviewer:  ma.reviewer,
		Filter:    ma.filter,
		Segment:   ma.segment,
		Species:   ma.species,
		Certainty: ma.certainty,
		Comment:   ma.comment,
	}
	if ma.bookmark {
		input.Bookmark = &ma.bookmark
	}

	result, err := tools.CallsModify(input)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", result.Error)
		os.Exit(1)
	}

	data, _ := json.Marshal(result)
	fmt.Println(string(data))
}