package tools

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/patriceckhart/zot/packages/core"
	"github.com/patriceckhart/zot/packages/provider"
)

// WriteTool writes content to a file, creating parent directories.
type WriteTool struct {
	CWD     string
	Sandbox *Sandbox
}

type writeArgs struct {
	Path    string `json:"path"`
	Content string `json:"content"`
}

const writeSchema = `{"type":"object","properties":{"path":{"type":"string"},"content":{"type":"string"}},"required":["path","content"]}`

func (t *WriteTool) Name() string { return "write" }
func (t *WriteTool) Description() string {
	return "Write a file. Creates parent dirs. Overwrites."
}
func (t *WriteTool) Schema() json.RawMessage { return json.RawMessage(writeSchema) }

func (t *WriteTool) Execute(ctx context.Context, raw json.RawMessage, progress func(string)) (core.ToolResult, error) {
	var a writeArgs
	if err := json.Unmarshal(raw, &a); err != nil {
		return core.ToolResult{}, fmt.Errorf("invalid args: %w", err)
	}
	if a.Path == "" {
		return core.ToolResult{}, fmt.Errorf("path is required")
	}
	path := resolvePath(t.CWD, a.Path)
	if err := t.Sandbox.CheckPath(path); err != nil {
		return core.ToolResult{}, err
	}

	if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
		return core.ToolResult{}, err
	}
	if err := os.WriteFile(path, []byte(a.Content), 0o644); err != nil {
		return core.ToolResult{}, err
	}

	// Return the file content as the result body, just like `read`
	// does. The TUI renders it with a syntax-highlighted gutter so
	// the on-screen view after a `write` matches the pre-write
	// streaming preview seamlessly. The model also sees the written
	// content in its tool_result, which is useful on follow-up turns
	// where it wants to reference what it just wrote without a
	// second `read` call.
	totalLines := strings.Count(a.Content, "\n")
	if len(a.Content) > 0 && !strings.HasSuffix(a.Content, "\n") {
		totalLines++ // count the last unterminated line
	}
	return core.ToolResult{
		Content: []provider.Content{provider.TextBlock{Text: a.Content}},
		Details: map[string]any{
			"path":        path,
			"bytes":       len(a.Content),
			"total_lines": totalLines,
			"start_line":  1,
		},
	}, nil
}
