package agent

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/tidbcloud/AgentTeam/desktop/internal/identitydoc"
)

// DocKind identifies one workspace-backed agent document.
type DocKind string

const (
	// DocKindIdentity is the always-on identity document.
	DocKindIdentity DocKind = "identity"
)

// docFileMap maps DocKind to its on-disk filename.
var docFileMap = map[DocKind]string{
	DocKindIdentity: identitydoc.ActiveIdentityPath,
}

// ParseDocKind validates s and returns its DocKind. Returns an error when
// the value does not match any known kind.
func ParseDocKind(s string) (DocKind, error) {
	switch DocKind(s) {
	case DocKindIdentity:
		return DocKind(s), nil
	default:
		return "", fmt.Errorf("unknown doc kind %q", s)
	}
}

// DocFilename returns the authoritative on-disk filename for kind.
func DocFilename(kind DocKind) (string, error) {
	name, ok := docFileMap[kind]
	if !ok {
		return "", fmt.Errorf("unknown doc kind %q", kind)
	}
	return name, nil
}

// ReadDoc returns the content of a doc inside workDir, or empty string when
// the file does not exist. Other read errors are returned as-is.
func ReadDoc(workDir string, kind DocKind) (string, error) {
	if _, err := DocFilename(kind); err != nil {
		return "", err
	}
	if kind == DocKindIdentity {
		_, data, ok, err := identitydoc.ReadActive(workDir)
		if err != nil || !ok {
			return "", err
		}
		return string(data), nil
	}
	return "", nil
}

// WriteDoc atomically writes content to a doc inside workDir, creating the
// directory on demand.
func WriteDoc(workDir string, kind DocKind, content string) error {
	if _, err := DocFilename(kind); err != nil {
		return err
	}
	target := identitydoc.ActiveIdentityPath
	targetPath := filepath.Join(workDir, target)
	if err := os.MkdirAll(filepath.Dir(targetPath), 0o755); err != nil {
		return err
	}
	return atomicWrite(targetPath, []byte(content))
}

// atomicWrite writes data to path via a temp file + rename so concurrent
// readers never observe partial content.
func atomicWrite(path string, data []byte) error {
	dir := filepath.Dir(path)
	tmp, err := os.CreateTemp(dir, ".doc-*.tmp")
	if err != nil {
		return err
	}
	tmpName := tmp.Name()
	defer os.Remove(tmpName)
	if _, err := tmp.Write(data); err != nil {
		logIgnoredCleanupError("failed to close agent docs temp file after write error", tmp.Close(), "path", tmpName)
		return err
	}
	if err := tmp.Close(); err != nil {
		return err
	}
	return os.Rename(tmpName, path)
}
