package winlimit

import (
	"cursor-api-proxy/internal/env"
	"runtime"
)

const WinPromptOmissionPrefix = "[Earlier messages omitted: Windows command-line length limit.]\n\n"
const LinuxPromptOmissionPrefix = "[Earlier messages omitted: Linux ARG_MAX command-line length limit.]\n\n"

// safeLinuxArgMax returns a conservative estimate of ARG_MAX on Linux.
// The actual limit is typically 2MB; we use 1.5MB to leave room for env vars.
func safeLinuxArgMax() int {
	return 1536 * 1024
}

type FitPromptResult struct {
	OK                bool
	Args              []string
	Truncated         bool
	OriginalLength    int
	FinalPromptLength int
	Error             string
}

func estimateCmdlineLength(resolved env.AgentCommand) int {
	argv := append([]string{resolved.Command}, resolved.Args...)
	if resolved.WindowsVerbatimArguments {
		n := 0
		for _, a := range argv {
			n += len(a)
		}
		if len(argv) > 1 {
			n += len(argv) - 1
		}
		return n + 512
	}
	dstLen := 0
	for _, a := range argv {
		dstLen += len(a)
	}
	dstLen = dstLen*2 + len(argv)*2
	if len(argv) > 1 {
		dstLen += len(argv) - 1
	}
	return dstLen + 512
}

func FitPromptToWinCmdline(agentBin string, fixedArgs []string, prompt string, maxCmdline int, cwd string) FitPromptResult {
	if runtime.GOOS != "windows" {
		return fitPromptLinux(fixedArgs, prompt)
	}

	e := env.OsEnvToMap()
	measured := func(p string) int {
		args := make([]string, len(fixedArgs)+1)
		copy(args, fixedArgs)
		args[len(fixedArgs)] = p
		resolved := env.ResolveAgentCommand(agentBin, args, e, cwd)
		return estimateCmdlineLength(resolved)
	}

	if measured("") > maxCmdline {
		return FitPromptResult{
			OK:    false,
			Error: "Windows command line exceeds the configured limit even without a prompt; shorten workspace path, model id, or CURSOR_BRIDGE_WIN_CMDLINE_MAX.",
		}
	}

	if measured(prompt) <= maxCmdline {
		args := make([]string, len(fixedArgs)+1)
		copy(args, fixedArgs)
		args[len(fixedArgs)] = prompt
		return FitPromptResult{
			OK:                true,
			Args:              args,
			Truncated:         false,
			OriginalLength:    len(prompt),
			FinalPromptLength: len(prompt),
		}
	}

	prefix := WinPromptOmissionPrefix
	if measured(prefix) > maxCmdline {
		return FitPromptResult{
			OK:    false,
			Error: "Windows command line too long to fit even the truncation notice; shorten workspace path or flags.",
		}
	}

	lo, hi, best := 0, len(prompt), 0
	for lo <= hi {
		mid := (lo + hi) / 2
		var tail string
		if mid > 0 {
			tail = prompt[len(prompt)-mid:]
		}
		candidate := prefix + tail
		if measured(candidate) <= maxCmdline {
			best = mid
			lo = mid + 1
		} else {
			hi = mid - 1
		}
	}

	var finalPrompt string
	if best == 0 {
		finalPrompt = prefix
	} else {
		finalPrompt = prefix + prompt[len(prompt)-best:]
	}

	args := make([]string, len(fixedArgs)+1)
	copy(args, fixedArgs)
	args[len(fixedArgs)] = finalPrompt
	return FitPromptResult{
		OK:                true,
		Args:              args,
		Truncated:         true,
		OriginalLength:    len(prompt),
		FinalPromptLength: len(finalPrompt),
	}
}

// fitPromptLinux handles Linux ARG_MAX truncation.
func fitPromptLinux(fixedArgs []string, prompt string) FitPromptResult {
	argMax := safeLinuxArgMax()

	// Estimate total cmdline size: sum of all fixed args + prompt + null terminators
	fixedLen := 0
	for _, a := range fixedArgs {
		fixedLen += len(a) + 1
	}
	totalLen := fixedLen + len(prompt) + 1

	if totalLen <= argMax {
		args := make([]string, len(fixedArgs)+1)
		copy(args, fixedArgs)
		args[len(fixedArgs)] = prompt
		return FitPromptResult{
			OK:                true,
			Args:              args,
			Truncated:         false,
			OriginalLength:    len(prompt),
			FinalPromptLength: len(prompt),
		}
	}

	// Need to truncate: keep the tail of the prompt (most recent messages)
	prefix := LinuxPromptOmissionPrefix
	available := argMax - fixedLen - len(prefix) - 1
	if available < 0 {
		available = 0
	}

	var finalPrompt string
	if available <= 0 {
		finalPrompt = prefix
	} else if available >= len(prompt) {
		finalPrompt = prefix + prompt
	} else {
		finalPrompt = prefix + prompt[len(prompt)-available:]
	}

	args := make([]string, len(fixedArgs)+1)
	copy(args, fixedArgs)
	args[len(fixedArgs)] = finalPrompt
	return FitPromptResult{
		OK:                true,
		Args:              args,
		Truncated:         true,
		OriginalLength:    len(prompt),
		FinalPromptLength: len(finalPrompt),
	}
}

func WarnPromptTruncated(originalLength, finalLength int) {
	_ = originalLength
	_ = finalLength
}
