package cmd

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"

	"skraak/tools"
)

// RunCalls handles the "calls" command
func RunCalls(args []string) {
	if len(args) < 1 {
		printCallsUsage()
		os.Exit(1)
	}

	switch args[0] {
	case "from-preds":
		runCallsFromPreds(args[1:])
	case "show-images":
		runCallsShowImages(args[1:])
	case "classify":
		RunCallsClassify(args[1:])
	default:
		fmt.Fprintf(os.Stderr, "Unknown calls subcommand: %s\n\n", args[0])
		printCallsUsage()
		os.Exit(1)
	}
}

func printCallsUsage() {
	fmt.Fprintf(os.Stderr, "Usage: skraak calls <subcommand> [options]\n\n")
	fmt.Fprintf(os.Stderr, "Subcommands:\n")
	fmt.Fprintf(os.Stderr, "  from-preds   Extract clustered calls from ML predictions CSV\n")
	fmt.Fprintf(os.Stderr, "  show-images  Display spectrogram images from .data file\n")
	fmt.Fprintf(os.Stderr, "  classify     Review and classify segments in .data files\n")
	fmt.Fprintf(os.Stderr, "\nExamples:\n")
	fmt.Fprintf(os.Stderr, "  skraak calls from-preds --csv predictions.csv\n")
	fmt.Fprintf(os.Stderr, "  skraak calls from-preds --csv preds.csv --dot-data=false > calls.json\n")
	fmt.Fprintf(os.Stderr, "  skraak calls from-preds --csv preds.csv --filter my-filter\n")
	fmt.Fprintf(os.Stderr, "  skraak calls show-images --file recording.wav.data\n")
	fmt.Fprintf(os.Stderr, "  skraak calls classify --folder ./data --reviewer David --bind k=Kiwi\n")
	fmt.Fprintf(os.Stderr, "  skraak calls classify --folder ./data --reviewer David \\\n")
	fmt.Fprintf(os.Stderr, "      --bind k=Kiwi --bind d='Kiwi+Duet' --bind n='Don''t Know'\n")
}

// runCallsFromPreds handles the "calls from-preds" subcommand
func runCallsFromPreds(args []string) {
	fs := flag.NewFlagSet("calls from-preds", flag.ExitOnError)
	csvPath := fs.String("csv", "", "Path to predictions CSV file (required)")
	filter := fs.String("filter", "", "Filter name for .data files (default: parse from CSV filename)")
	dotData := fs.Bool("dot-data", true, "Write .data files alongside audio files (default: true)")

	fs.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: skraak calls from-preds [options]\n\n")
		fmt.Fprintf(os.Stderr, "Extract clustered bird calls from ML predictions CSV.\n")
		fmt.Fprintf(os.Stderr, "Reads prediction CSV with columns: file, start_time, end_time, <ebird_codes...>\n")
		fmt.Fprintf(os.Stderr, "Each row is a clip with 1=present, 0=absent for each species.\n\n")
		fmt.Fprintf(os.Stderr, "Options:\n")
		fs.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\nOutput:\n")
		fmt.Fprintf(os.Stderr, "  With --dot-data=true (default): Writes .data files alongside audio files, outputs JSON summary\n")
		fmt.Fprintf(os.Stderr, "  With --dot-data=false: Outputs JSON with clustered calls only (no .data files)\n")
		fmt.Fprintf(os.Stderr, "\nFilter name:\n")
		fmt.Fprintf(os.Stderr, "  If --filter is provided, uses that value.\n")
		fmt.Fprintf(os.Stderr, "  Otherwise, parses from CSV filename: prefix_filter_date.csv -> filter\n")
		fmt.Fprintf(os.Stderr, "  Example: predsST_opensoundscape-kiwi-1.2_2025-11-12.csv -> opensoundscape-kiwi-1.2\n")
		fmt.Fprintf(os.Stderr, "\nExamples:\n")
		fmt.Fprintf(os.Stderr, "  # Write .data files (default)\n")
		fmt.Fprintf(os.Stderr, "  skraak calls from-preds --csv predictions.csv\n")
		fmt.Fprintf(os.Stderr, "\n")
		fmt.Fprintf(os.Stderr, "  # JSON output only (no .data files)\n")
		fmt.Fprintf(os.Stderr, "  skraak calls from-preds --csv predictions.csv --dot-data=false > calls.json\n")
		fmt.Fprintf(os.Stderr, "\n")
		fmt.Fprintf(os.Stderr, "  # Override filter name\n")
		fmt.Fprintf(os.Stderr, "  skraak calls from-preds --csv preds.csv --filter my-custom-filter\n")
	}

	if err := fs.Parse(args); err != nil {
		os.Exit(1)
	}

	// Validate required flags
	if *csvPath == "" {
		fmt.Fprintf(os.Stderr, "Error: --csv is required\n\n")
		fs.Usage()
		os.Exit(1)
	}

	// Determine filter name
	filterName := *filter
	if filterName == "" {
		filterName = tools.ParseFilterFromFilename(*csvPath)
		if filterName == "" {
			fmt.Fprintf(os.Stderr, "Error: Could not parse filter from filename. Use --filter flag.\n")
			fmt.Fprintf(os.Stderr, "Expected format: prefix_filter_date.csv (e.g., predsST_opensoundscape-kiwi-1.2_2025-11-12.csv)\n")
			os.Exit(1)
		}
	}

	input := tools.CallsFromPredsInput{
		CSVPath:      *csvPath,
		Filter:       filterName,
		WriteDotData: *dotData,
		ProgressHandler: func(processed, total int, message string) {
			if total > 0 {
				percent := float64(processed) / float64(total) * 100
				fmt.Fprintf(os.Stderr, "\rProcessing WAV files: %d/%d (%.0f%%)", processed, total, percent)
				if processed == total {
					fmt.Fprintf(os.Stderr, "\n")
				}
			}
		},
	}

	if *dotData {
		fmt.Fprintf(os.Stderr, "Extracting calls from predictions: %s\n", *csvPath)
		fmt.Fprintf(os.Stderr, "Filter: %s\n", filterName)
		fmt.Fprintf(os.Stderr, "Writing .data files: enabled\n")
	} else {
		fmt.Fprintf(os.Stderr, "Extracting calls from predictions: %s\n", *csvPath)
		fmt.Fprintf(os.Stderr, "Filter: %s\n", filterName)
		fmt.Fprintf(os.Stderr, "Writing .data files: disabled (--dot-data=false)\n")
	}

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

	fmt.Fprintf(os.Stderr, "Found %d clustered calls across %d species\n",
		output.TotalCalls, len(output.SpeciesCount))
	fmt.Fprintf(os.Stderr, "Clip duration: %.1fs, Gap threshold: %.1fs\n",
		output.ClipDuration, output.GapThreshold)

	if *dotData {
		fmt.Fprintf(os.Stderr, "Data files written: %d, skipped: %d\n",
			output.DataFilesWritten, output.DataFilesSkipped)
	}

	// Output JSON to stdout
	enc := json.NewEncoder(os.Stdout)
	enc.SetIndent("", "  ")
	enc.Encode(output)
}

// runCallsShowImages handles the "calls show-images" subcommand
func runCallsShowImages(args []string) {
	fs := flag.NewFlagSet("calls show-images", flag.ExitOnError)
	filePath := fs.String("file", "", "Path to .data file (required)")
	color := fs.Bool("color", false, "Apply L4 colormap (default: false, grayscale)")
	imgDims := fs.Int("img-dims", 0, "Spectrogram size in pixels (224-448, default 448)")
	sixel := fs.Bool("sixel", false, "Use sixel graphics protocol (default: kitty)")
	iterm := fs.Bool("iterm", false, "Use iTerm2 inline image protocol")

	fs.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: skraak calls show-images [options]\n\n")
		fmt.Fprintf(os.Stderr, "Display spectrogram images for each segment in a .data file.\n")
		fmt.Fprintf(os.Stderr, "Images are output using the Kitty graphics protocol (or Sixel with --sixel, iTerm2 with --iterm).\n\n")
		fmt.Fprintf(os.Stderr, "Options:\n")
		fs.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\nExamples:\n")
		fmt.Fprintf(os.Stderr, "  skraak calls show-images --file recording.wav.data\n")
		fmt.Fprintf(os.Stderr, "  skraak calls show-images --file recording.wav.data --color\n")
	}

	if err := fs.Parse(args); err != nil {
		os.Exit(1)
	}

	// Validate required flags
	if *filePath == "" {
		fmt.Fprintf(os.Stderr, "Error: --file is required\n\n")
		fs.Usage()
		os.Exit(1)
	}

	input := tools.CallsShowImagesInput{
		DataFilePath: *filePath,
		Color:        *color,
		ImageSize:    *imgDims,
		Sixel:        *sixel,
		ITerm:        *iterm,
	}

	fmt.Fprintf(os.Stderr, "Showing spectrogram images for: %s\n", *filePath)
	if *color {
		fmt.Fprintf(os.Stderr, "Color: L4 colormap (Black-Red-Yellow)\n")
	}

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

	fmt.Fprintf(os.Stderr, "Displayed %d segment(s) from %s\n", output.SegmentsShown, output.WavFile)
}