package agent

import (
	"fmt"
	"log/slog"
	"strings"
	"sync"

	"github.com/tidbcloud/AgentTeam/desktop/internal/agent/drivercontract"
	"github.com/tidbcloud/AgentTeam/desktop/internal/agent/runtimeconfig"
)

// NewDriver creates a runtime-specific driver for the given runtime type.
func NewDriver(rt runtimeconfig.RuntimeType) (drivercontract.Driver, error) {
	switch rt {
	case runtimeconfig.TypeOpenCode:
		return &OpenCodeDriver{}, nil
	case runtimeconfig.TypeClaudeCode:
		return &ClaudeDriver{}, nil
	case runtimeconfig.TypeCodex:
		return &CodexAppServerDriver{}, nil
	case runtimeconfig.TypeKimi:
		return &KimiACPDriver{}, nil
	case runtimeconfig.TypePi:
		return &PiRPCDriver{}, nil
	case runtimeconfig.TypeBuiltin:
		return &OpenCodeDriver{}, nil
	default:
		return nil, fmt.Errorf("unsupported interactive runtime type %q", rt)
	}
}

func firstNonEmptyString(values ...string) string {
	for _, value := range values {
		if value != "" {
			return value
		}
	}
	return ""
}

// prefixWriter logs each written line with a fixed prefix.
type prefixWriter struct {
	prefix string
	buf    []byte
	onLine func(string)
}

func (pw *prefixWriter) Write(p []byte) (int, error) {
	pw.buf = append(pw.buf, p...)
	for {
		idx := -1
		for i, b := range pw.buf {
			if b == '\n' {
				idx = i
				break
			}
		}
		if idx < 0 {
			break
		}
		line := pw.buf[:idx]
		if len(line) > 0 {
			lineText := string(line)
			slog.Info(lineText, "prefix", pw.prefix)
			if pw.onLine != nil {
				pw.onLine(lineText)
			}
		}
		pw.buf = pw.buf[idx+1:]
	}
	return len(p), nil
}

type capturingPrefixWriter struct {
	mu      sync.Mutex
	prefix  string
	buf     []byte
	tail    []byte
	maxTail int
	onLine  func(string)
}

func newCapturingPrefixWriter(prefix string, maxTail int) *capturingPrefixWriter {
	return &capturingPrefixWriter{prefix: prefix, maxTail: maxTail}
}

func (pw *capturingPrefixWriter) Write(p []byte) (int, error) {
	pw.mu.Lock()
	defer pw.mu.Unlock()

	pw.tail = append(pw.tail, p...)
	if pw.maxTail > 0 && len(pw.tail) > pw.maxTail {
		pw.tail = pw.tail[len(pw.tail)-pw.maxTail:]
	}

	pw.buf = append(pw.buf, p...)
	for {
		idx := -1
		for i, b := range pw.buf {
			if b == '\n' {
				idx = i
				break
			}
		}
		if idx < 0 {
			break
		}
		line := pw.buf[:idx]
		if len(line) > 0 {
			lineText := string(line)
			slog.Info(lineText, "prefix", pw.prefix)
			if pw.onLine != nil {
				pw.onLine(lineText)
			}
		}
		pw.buf = pw.buf[idx+1:]
	}
	return len(p), nil
}

func (pw *capturingPrefixWriter) Tail() string {
	if pw == nil {
		return ""
	}
	pw.mu.Lock()
	defer pw.mu.Unlock()
	return strings.TrimSpace(string(pw.tail))
}
