// Package connection provides an abstraction for local and remote operations.
// This allows Gas Town to manage rigs on remote machines via SSH using
// the same interface as local operations.
package connection

import (
	"io/fs"
	"time"
)

// Connection abstracts file operations, command execution, and tmux management
// for both local and remote (SSH) execution contexts.
type Connection interface {
	// Identification

	// Name returns a human-readable name for this connection.
	Name() string

	// IsLocal returns true if this is a local connection.
	IsLocal() bool

	// File operations

	// ReadFile reads the named file and returns its contents.
	ReadFile(path string) ([]byte, error)

	// WriteFile writes data to the named file with the given permissions.
	WriteFile(path string, data []byte, perm fs.FileMode) error

	// MkdirAll creates a directory and all parent directories.
	MkdirAll(path string, perm fs.FileMode) error

	// Remove removes the named file or empty directory.
	Remove(path string) error

	// RemoveAll removes the named file or directory and any children.
	RemoveAll(path string) error

	// Stat returns file info for the named file.
	Stat(path string) (FileInfo, error)

	// Glob returns the names of all files matching the pattern.
	Glob(pattern string) ([]string, error)

	// Exists returns true if the path exists.
	Exists(path string) (bool, error)

	// Command execution

	// Exec runs a command and returns its combined output.
	Exec(cmd string, args ...string) ([]byte, error)

	// ExecDir runs a command in the specified directory.
	ExecDir(dir, cmd string, args ...string) ([]byte, error)

	// ExecEnv runs a command with additional environment variables.
	ExecEnv(env map[string]string, cmd string, args ...string) ([]byte, error)

	// Tmux operations

	// TmuxNewSession creates a new tmux session with the given name.
	TmuxNewSession(name, dir string) error

	// TmuxKillSession terminates the named tmux session.
	TmuxKillSession(name string) error

	// TmuxSendKeys sends keys to the named tmux session.
	TmuxSendKeys(session, keys string) error

	// TmuxCapturePane captures the last N lines from a tmux pane.
	TmuxCapturePane(session string, lines int) (string, error)

	// TmuxHasSession returns true if the named tmux session exists.
	TmuxHasSession(name string) (bool, error)

	// TmuxListSessions returns a list of all tmux session names.
	TmuxListSessions() ([]string, error)
}

// FileInfo abstracts fs.FileInfo for use over remote connections.
// This is needed because fs.FileInfo contains methods that can't be
// easily serialized over SSH.
type FileInfo interface {
	// Name returns the base name of the file.
	Name() string

	// Size returns the length in bytes.
	Size() int64

	// Mode returns the file mode bits.
	Mode() fs.FileMode

	// ModTime returns the modification time.
	ModTime() time.Time

	// IsDir returns true if this is a directory.
	IsDir() bool
}

// BasicFileInfo is a simple implementation of FileInfo.
type BasicFileInfo struct {
	FileName    string      `json:"name"`
	FileSize    int64       `json:"size"`
	FileMode    fs.FileMode `json:"mode"`
	FileModTime time.Time   `json:"mod_time"`
	FileIsDir   bool        `json:"is_dir"`
}

// Name implements FileInfo.
func (f BasicFileInfo) Name() string { return f.FileName }

// Size implements FileInfo.
func (f BasicFileInfo) Size() int64 { return f.FileSize }

// Mode implements FileInfo.
func (f BasicFileInfo) Mode() fs.FileMode { return f.FileMode }

// ModTime implements FileInfo.
func (f BasicFileInfo) ModTime() time.Time { return f.FileModTime }

// IsDir implements FileInfo.
func (f BasicFileInfo) IsDir() bool { return f.FileIsDir }

// FromOSFileInfo creates a BasicFileInfo from an os.FileInfo.
func FromOSFileInfo(fi fs.FileInfo) BasicFileInfo {
	return BasicFileInfo{
		FileName:    fi.Name(),
		FileSize:    fi.Size(),
		FileMode:    fi.Mode(),
		FileModTime: fi.ModTime(),
		FileIsDir:   fi.IsDir(),
	}
}

// Error types for connection operations.
type (
	// ConnectionError indicates a connection-level failure.
	ConnectionError struct {
		Op      string // Operation that failed (e.g., "connect", "exec")
		Machine string // Machine name or address
		Err     error  // Underlying error
	}

	// NotFoundError indicates a file or resource was not found.
	NotFoundError struct {
		Path string
	}

	// PermissionError indicates an access permission failure.
	PermissionError struct {
		Path string
		Op   string
	}
)

func (e *ConnectionError) Error() string {
	return "connection " + e.Op + " on " + e.Machine + ": " + e.Err.Error()
}

func (e *ConnectionError) Unwrap() error {
	return e.Err
}

func (e *NotFoundError) Error() string {
	return "not found: " + e.Path
}

func (e *PermissionError) Error() string {
	return "permission denied: " + e.Op + " " + e.Path
}
