package logger

import (
	"cursor-api-proxy/internal/config"
	"cursor-api-proxy/internal/pool"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"
)

const (
	cReset    = "\x1b[0m"
	cBold     = "\x1b[1m"
	cDim      = "\x1b[2m"
	cCyan     = "\x1b[36m"
	cBCyan    = "\x1b[1;96m"
	cGreen    = "\x1b[32m"
	cBGreen   = "\x1b[1;92m"
	cYellow   = "\x1b[33m"
	cMagenta  = "\x1b[35m"
	cBMagenta = "\x1b[1;95m"
	cRed      = "\x1b[31m"
	cGray     = "\x1b[90m"
	cWhite    = "\x1b[97m"
)

var roleStyle = map[string]string{
	"system":    cYellow,
	"user":      cCyan,
	"assistant": cGreen,
}

var roleEmoji = map[string]string{
	"system":    "🔧",
	"user":      "👤",
	"assistant": "🤖",
}

func ts() string {
	return cGray + time.Now().UTC().Format("15:04:05") + cReset
}

func tsDate() string {
	return cGray + time.Now().UTC().Format("2006-01-02 15:04:05") + cReset
}

func truncate(s string, max int) string {
	if len(s) <= max {
		return s
	}
	head := int(float64(max) * 0.6)
	tail := max - head
	omitted := len(s) - head - tail
	return s[:head] + fmt.Sprintf("%s … (%d chars omitted) … ", cDim, omitted) + s[len(s)-tail:] + cReset
}

func hr(ch string, length int) string {
	return cGray + strings.Repeat(ch, length) + cReset
}

type TrafficMessage struct {
	Role    string
	Content string
}

func LogDebug(format string, args ...interface{}) {
	msg := fmt.Sprintf(format, args...)
	fmt.Printf("%s %s[DEBUG]%s %s\n", ts(), cGray, cReset, msg)
}

func LogServerStart(version, scheme, host string, port int, cfg config.BridgeConfig) {
	fmt.Printf("\n%s%s╔══════════════════════════════════════════╗%s\n", cBold, cBCyan, cReset)
	fmt.Printf("%s%s  cursor-api-proxy %sv%s%s%s%s  ready%s\n",
		cBold, cBCyan, cReset, cBold, cWhite, version, cBCyan, cReset)
	fmt.Printf("%s%s╚══════════════════════════════════════════╝%s\n\n", cBold, cBCyan, cReset)
	url := fmt.Sprintf("%s://%s:%d", scheme, host, port)
	fmt.Printf("  %s●%s listening  %s%s%s\n", cBGreen, cReset, cBold, url, cReset)
	fmt.Printf("  %s▸%s agent      %s%s%s\n", cCyan, cReset, cDim, cfg.AgentBin, cReset)
	fmt.Printf("  %s▸%s workspace  %s%s%s\n", cCyan, cReset, cDim, cfg.Workspace, cReset)
	fmt.Printf("  %s▸%s model      %s%s%s\n", cCyan, cReset, cDim, cfg.DefaultModel, cReset)
	fmt.Printf("  %s▸%s mode       %s%s%s\n", cCyan, cReset, cDim, cfg.Mode, cReset)
	fmt.Printf("  %s▸%s timeout    %s%d ms%s\n", cCyan, cReset, cDim, cfg.TimeoutMs, cReset)

	flags := []string{}
	if cfg.Force {
		flags = append(flags, "force")
	}
	if cfg.ApproveMcps {
		flags = append(flags, "approve-mcps")
	}
	if cfg.MaxMode {
		flags = append(flags, "max-mode")
	}
	if cfg.Verbose {
		flags = append(flags, "verbose")
	}
	if cfg.ChatOnlyWorkspace {
		flags = append(flags, "chat-only")
	}
	if cfg.RequiredKey != "" {
		flags = append(flags, "api-key-required")
	}
	if len(flags) > 0 {
		fmt.Printf("  %s▸%s flags      %s%s%s\n", cCyan, cReset, cYellow, strings.Join(flags, " · "), cReset)
	}
	if len(cfg.ConfigDirs) > 0 {
		fmt.Printf("  %s▸%s pool       %s%d accounts%s\n", cCyan, cReset, cBGreen, len(cfg.ConfigDirs), cReset)
	}
	fmt.Println()
}

func LogShutdown(sig string) {
	fmt.Printf("\n%s %s⊘ %s received — shutting down gracefully…%s\n", tsDate(), cYellow, sig, cReset)
}

func LogRequestStart(method, pathname, model string, timeoutMs int, isStream bool) {
	modeTag := fmt.Sprintf("%ssync%s", cDim, cReset)
	if isStream {
		modeTag = fmt.Sprintf("%s⚡ stream%s", cBCyan, cReset)
	}
	fmt.Printf("%s %s▶%s %s %s %s  timeout:%dms  %s\n",
		ts(), cBCyan, cReset, method, pathname, model, timeoutMs, modeTag)
}

func LogRequestDone(method, pathname, model string, latencyMs int64, code int) {
	statusColor := cBGreen
	if code != 0 {
		statusColor = cRed
	}
	fmt.Printf("%s %s■%s %s %s %s  %s%dms exit:%d%s\n",
		ts(), statusColor, cReset, method, pathname, model, cDim, latencyMs, code, cReset)
}

func LogRequestTimeout(method, pathname, model string, timeoutMs int) {
	fmt.Printf("%s %s⏱%s %s %s %s  %stimed-out after %dms%s\n",
		ts(), cRed, cReset, method, pathname, model, cRed, timeoutMs, cReset)
}

func LogClientDisconnect(method, pathname, model string, latencyMs int64) {
	fmt.Printf("%s %s⚡%s %s %s %s  %sclient disconnected after %dms%s\n",
		ts(), cYellow, cReset, method, pathname, model, cYellow, latencyMs, cReset)
}

func LogStreamChunk(model string, text string, chunkNum int) {
	preview := truncate(strings.ReplaceAll(text, "\n", "↵ "), 120)
	fmt.Printf("%s %s▸%s #%d %s%s%s\n",
		ts(), cDim, cReset, chunkNum, cWhite, preview, cReset)
}

func LogRawLine(line string) {
	preview := truncate(strings.ReplaceAll(line, "\n", "↵ "), 200)
	fmt.Printf("%s %s│%s %sraw%s %s\n",
		ts(), cGray, cReset, cDim, cReset, preview)
}

func LogIncoming(method, pathname, remoteAddress string) {
	methodColor := cBCyan
	switch method {
	case "POST":
		methodColor = cBMagenta
	case "GET":
		methodColor = cBCyan
	case "DELETE":
		methodColor = cRed
	}
	fmt.Printf("%s %s%s%s%s %s%s%s %s(%s)%s\n",
		ts(),
		methodColor, cBold, method, cReset,
		cWhite, pathname, cReset,
		cDim, remoteAddress, cReset,
	)
}

func LogAccountAssigned(configDir string) {
	if configDir == "" {
		return
	}
	name := filepath.Base(configDir)
	fmt.Printf("%s %s→%s account %s%s%s\n", ts(), cBCyan, cReset, cBold, name, cReset)
}

func LogAccountStats(verbose bool, stats []pool.AccountStat) {
	if !verbose || len(stats) == 0 {
		return
	}
	now := time.Now().UnixMilli()
	fmt.Printf("%s┌─ Account Stats %s┐%s\n", cGray, strings.Repeat("─", 44), cReset)
	for _, s := range stats {
		name := fmt.Sprintf("%-20s", filepath.Base(s.ConfigDir))
		active := fmt.Sprintf("%sactive:0%s", cDim, cReset)
		if s.ActiveRequests > 0 {
			active = fmt.Sprintf("%sactive:%d%s", cBCyan, s.ActiveRequests, cReset)
		}
		total := fmt.Sprintf("total:%s%d%s", cBold, s.TotalRequests, cReset)
		ok := fmt.Sprintf("%sok:%d%s", cGreen, s.TotalSuccess, cReset)
		errStr := fmt.Sprintf("%serr:0%s", cDim, cReset)
		if s.TotalErrors > 0 {
			errStr = fmt.Sprintf("%serr:%d%s", cRed, s.TotalErrors, cReset)
		}
		rl := fmt.Sprintf("%srl:0%s", cDim, cReset)
		if s.TotalRateLimits > 0 {
			rl = fmt.Sprintf("%srl:%d%s", cYellow, s.TotalRateLimits, cReset)
		}
		avg := "avg:-"
		if s.TotalRequests > 0 {
			avg = fmt.Sprintf("avg:%dms", s.TotalLatencyMs/int64(s.TotalRequests))
		}
		status := fmt.Sprintf("%s✓%s", cGreen, cReset)
		if s.IsRateLimited {
			recovers := time.UnixMilli(s.RateLimitUntil).UTC().Format(time.RFC3339)
			_ = now
			status = fmt.Sprintf("%s⛔ rate-limited (recovers %s)%s", cRed, recovers, cReset)
		}
		fmt.Printf("  %s%s%s  %s  %s  %s  %s  %s  %s%s%s  %s\n",
			cBold, name, cReset, active, total, ok, errStr, rl, cDim, avg, cReset, status)
	}
	fmt.Printf("%s└%s┘%s\n", cGray, strings.Repeat("─", 60), cReset)
}

func LogTrafficRequest(verbose bool, model string, messages []TrafficMessage, isStream bool) {
	if !verbose {
		return
	}
	modeTag := fmt.Sprintf("%ssync%s", cDim, cReset)
	if isStream {
		modeTag = fmt.Sprintf("%s⚡ stream%s", cBCyan, cReset)
	}
	modelStr := fmt.Sprintf("%s✦ %s%s", cBMagenta, model, cReset)
	fmt.Println(hr("─", 60))
	fmt.Printf("%s 📤 %s%sREQUEST%s  %s  %s\n", ts(), cBCyan, cBold, cReset, modelStr, modeTag)
	for _, m := range messages {
		roleColor := cWhite
		if c, ok := roleStyle[m.Role]; ok {
			roleColor = c
		}
		emoji := "💬"
		if e, ok := roleEmoji[m.Role]; ok {
			emoji = e
		}
		label := fmt.Sprintf("%s%s[%s]%s", roleColor, cBold, m.Role, cReset)
		charCount := fmt.Sprintf("%s(%d chars)%s", cDim, len(m.Content), cReset)
		preview := truncate(strings.ReplaceAll(m.Content, "\n", "↵ "), 280)
		fmt.Printf("  %s %s %s\n", emoji, label, charCount)
		fmt.Printf("     %s%s%s\n", cDim, preview, cReset)
	}
}

func LogTrafficResponse(verbose bool, model, text string, isStream bool) {
	if !verbose {
		return
	}
	modeTag := fmt.Sprintf("%ssync%s", cDim, cReset)
	if isStream {
		modeTag = fmt.Sprintf("%s⚡ stream%s", cBGreen, cReset)
	}
	modelStr := fmt.Sprintf("%s✦ %s%s", cBMagenta, model, cReset)
	charCount := fmt.Sprintf("%s%d%s%s chars%s", cBold, len(text), cReset, cDim, cReset)
	preview := truncate(strings.ReplaceAll(text, "\n", "↵ "), 480)
	fmt.Printf("%s 📥 %s%sRESPONSE%s  %s  %s  %s\n", ts(), cBGreen, cBold, cReset, modelStr, modeTag, charCount)
	fmt.Printf("  🤖 %s%s%s\n", cGreen, preview, cReset)
	fmt.Println(hr("─", 60))
}

func AppendSessionLine(logPath, method, pathname, remoteAddress string, statusCode int) {
	line := fmt.Sprintf("%s %s %s %s %d\n", time.Now().UTC().Format(time.RFC3339), method, pathname, remoteAddress, statusCode)
	dir := filepath.Dir(logPath)
	if err := os.MkdirAll(dir, 0755); err == nil {
		f, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
		if err == nil {
			_, _ = f.WriteString(line)
			f.Close()
		}
	}
}

func LogTruncation(originalLen, finalLen int) {
	fmt.Printf("%s %s⚠ prompt truncated%s %s(%d → %d chars, tail preserved)%s\n",
		ts(), cYellow, cReset, cDim, originalLen, finalLen, cReset)
}

func LogAgentError(logPath, method, pathname, remoteAddress string, exitCode int, stderr string) string {
	errMsg := fmt.Sprintf("Cursor CLI failed (exit %d): %s", exitCode, strings.TrimSpace(stderr))
	fmt.Fprintf(os.Stderr, "%s %s✗ agent error%s %s%s%s\n", ts(), cRed, cReset, cDim, errMsg, cReset)
	truncated := strings.TrimSpace(stderr)
	if len(truncated) > 200 {
		truncated = truncated[:200]
	}
	truncated = strings.ReplaceAll(truncated, "\n", " ")
	line := fmt.Sprintf("%s ERROR %s %s %s agent_exit_%d %s\n",
		time.Now().UTC().Format(time.RFC3339), method, pathname, remoteAddress, exitCode, truncated)
	if f, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err == nil {
		_, _ = f.WriteString(line)
		f.Close()
	}
	return errMsg
}
