/*
 * Copyright 2025 CloudWeGo Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Package filesystem provides file system operations.
package filesystem

import (
	"context"

	"github.com/cloudwego/eino/schema"
)

// FileInfo represents basic file metadata information.
type FileInfo struct {
	// Path is the path of the file or directory, which can be a filename, relative path, or absolute path.
	Path string

	// IsDir indicates whether the entry is a directory.
	// true for directories, false for regular files.
	IsDir bool

	// Size is the file size in bytes.
	// For directories, this value may be 0 or platform-dependent.
	Size int64

	// ModifiedAt is the last modification time in ISO 8601 format.
	// Example: "2025-01-15T10:30:00Z"
	ModifiedAt string
}

// GrepMatch represents a single pattern match result.
type GrepMatch struct {
	Content string

	// Path is the file path where the match was found.
	Path string

	// Line is the 1-based line number of the match.
	Line int
}

// LsInfoRequest contains parameters for listing file information.
type LsInfoRequest struct {
	// Path specifies the directory path to list.
	Path string
}

// ReadRequest contains parameters for reading file content.
type ReadRequest struct {
	// FilePath is the path to the file to be read.
	FilePath string

	// Offset specifies the starting line number (1-based) for reading.
	// Line 1 is the first line of the file.
	// Use this when the file is too large to read at once.
	// Defaults to 1 (start from the first line).
	// Values < 1 will be treated as 1.
	Offset int

	// Limit specifies the maximum number of lines to read.
	// When Limit is 0 (default), the entire file content is returned.
	Limit int
}

// MultiModalReadRequest extends ReadRequest with parameters only applicable
// to MultiModalReader implementations (e.g. PDF page ranges).
type MultiModalReadRequest struct {
	ReadRequest

	// Pages specifies the page range for PDF files (e.g. "1-5", "3", "10-20").
	Pages string
}

// GrepRequest contains parameters for searching file content.
type GrepRequest struct {
	// ===== Search Parameters =====

	// Pattern is the search pattern, supports full regular expression syntax.
	// Uses ripgrep syntax (not grep). Examples:
	//   - "log.*Error" matches lines with "log" followed by "Error"
	//   - "function\\s+\\w+" matches "function" followed by whitespace and word characters
	//   - Literal braces need escaping: "interface\\{\\}" matches "interface{}"
	Pattern string

	// Path is an optional directory path to limit the search scope.
	Path string

	// ===== File Filtering =====

	// Glob is an optional pattern to filter the files to be searched.
	// It filters by file path, not content. If empty, no files are filtered.
	// Supports standard glob wildcards:
	//   - `*` matches any characters except path separators.
	//   - `**` matches any directories recursively.
	//   - `?` matches a single character.
	//   - `[abc]` matches one character from the set.
	Glob string

	// FileType is the file type filter, e.g., "js", "py", "rust".
	// More efficient than Glob for standard file types.
	FileType string

	// ===== Search Options =====

	// CaseInsensitive enables case insensitive search.
	CaseInsensitive bool

	// EnableMultiline enables multiline mode where patterns can span lines.
	// Default: false (patterns match within single lines only).
	EnableMultiline bool

	// ===== Context Display (Content mode only) =====

	// AfterLines shows N lines after each match.
	// Only applicable when OutputMode is "content".
	// Values <= 0 are treated as unset.
	AfterLines int

	// BeforeLines shows N lines before each match.
	// Only applicable when OutputMode is "content".
	// Values <= 0 are treated as unset.
	BeforeLines int
}

// GlobInfoRequest contains parameters for glob pattern matching.
type GlobInfoRequest struct {
	// Pattern is the glob expression used to match file paths.
	// It supports standard glob syntax:
	//   - `*` matches any characters except path separators.
	//   - `**` matches any directories recursively.
	//   - `?` matches a single character.
	//   - `[abc]` matches one character from the set.
	Pattern string

	// Path is the base directory from which to start the search.
	Path string
}

// WriteRequest contains parameters for writing file content.
type WriteRequest struct {
	// FilePath is the path of the file to write.
	FilePath string

	// Content is the data to be written to the file.
	Content string
}

// EditRequest contains parameters for editing file content.
type EditRequest struct {
	// FilePath is the path of the file to edit.
	FilePath string

	// OldString is the exact string to be replaced. It must be non-empty and will be matched literally, including whitespace.
	OldString string

	// NewString is the string that will replace OldString.
	// It must be different from OldString.
	// An empty string can be used to effectively delete OldString.
	NewString string

	// ReplaceAll controls the replacement behavior.
	// If true, all occurrences of OldString are replaced.
	// If false, the operation fails unless OldString appears exactly once in the file.
	ReplaceAll bool
}

// FileContentPartType defines the type of a multimodal file content part.
type FileContentPartType string

const (
	// FileContentPartTypeImage represents an image part (e.g. PNG, JPG).
	FileContentPartTypeImage FileContentPartType = "image"
	// FileContentPartTypePDF represents a file part (e.g. PDF).
	FileContentPartTypePDF FileContentPartType = "pdf"
)

// FileContentPart represents a multimodal part of file content.
// Data holds raw bytes; encoding (e.g. base64) is handled by the consumer.
type FileContentPart struct {
	// Type is the kind of content this part represents.
	// Required.
	Type FileContentPartType

	// MIMEType is the MIME type of the content (e.g. "image/png", "application/pdf").
	// Required.
	MIMEType string

	// Data is the raw binary content.
	// Required.
	Data []byte
}

// FileContent holds the result of a Read operation.
type FileContent struct {
	// Content holds the plain text content of the file.
	Content string
}

// MultiFileContent holds the result of a MultiModalRead operation.
//
// FileContent and Parts are mutually exclusive (one-of):
//   - Set FileContent for plain text results (same as a normal Read).
//   - Set Parts for multimodal results (images, PDFs, etc.).
//
// When Parts is non-empty, FileContent is ignored.
type MultiFileContent struct {
	*FileContent

	// Parts holds multimodal output parts (e.g. image, PDF).
	Parts []FileContentPart
}

// MultiModalReader is an optional extension interface for Backend.
// Backends that implement this interface support multimodal file reading,
// returning structured parts (images, PDFs) instead of plain text.
//
// For large file handling, there are two approaches to control output size:
//   - Implement size control within MultiModalRead (e.g. reject files exceeding a threshold,
//     downsample images, or limit PDF page counts at the backend level).
//   - Use ToolMiddleware's EnhancedInvokable to customize result transformation,
//     or use the built-in reduction middleware with configurable policies.
type MultiModalReader interface {
	MultiModalRead(ctx context.Context, req *MultiModalReadRequest) (*MultiFileContent, error)
}

// Backend is a pluggable, unified file backend protocol interface.
//
// All methods use struct-based parameters to allow future extensibility
// without breaking backward compatibility.
type Backend interface {
	// LsInfo lists file information under the given path.
	//
	// Returns:
	//   - []FileInfo: List of matching file information
	//   - error: Error if the operation fails
	LsInfo(ctx context.Context, req *LsInfoRequest) ([]FileInfo, error)

	// Read reads file content with support for line-based offset and limit.
	//
	// Returns:
	//   - string: The file content read
	//   - error: Error if file does not exist or read fails
	Read(ctx context.Context, req *ReadRequest) (*FileContent, error)

	// GrepRaw searches for content matching the specified pattern in files.
	//
	// Returns:
	//   - []GrepMatch: List of all matching results
	//   - error: Error if the search fails
	GrepRaw(ctx context.Context, req *GrepRequest) ([]GrepMatch, error)

	// GlobInfo returns file information matching the glob pattern.
	//
	// Returns:
	//   - []FileInfo: List of matching file information
	//   - error: Error if the pattern is invalid or operation fails
	GlobInfo(ctx context.Context, req *GlobInfoRequest) ([]FileInfo, error)

	// Write creates or updates file content.
	//
	// Returns:
	//   - error: Error if the write operation fails
	Write(ctx context.Context, req *WriteRequest) error

	// Edit replaces string occurrences in a file.
	//
	// Returns:
	//   - error: Error if file does not exist, OldString is empty, or OldString is not found
	Edit(ctx context.Context, req *EditRequest) error
}

// ExecuteRequest contains parameters for executing a command.
type ExecuteRequest struct {
	Command            string // The command to execute
	RunInBackendGround bool
}

// ExecuteResponse contains the response result of command execution.
type ExecuteResponse struct {
	Output    string // Command output content
	ExitCode  *int   // Command exit code
	Truncated bool   // Whether the output was truncated
}

type Shell interface {
	Execute(ctx context.Context, input *ExecuteRequest) (result *ExecuteResponse, err error)
}

type StreamingShell interface {
	ExecuteStreaming(ctx context.Context, input *ExecuteRequest) (result *schema.StreamReader[*ExecuteResponse], err error)
}
