package ipc

import (
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/bjarneo/cliamp/internal/appdir"
)

// DefaultSocketPath returns the default IPC socket path (~/.config/cliamp/cliamp.sock).
func DefaultSocketPath() string {
	dir, err := appdir.Dir()
	if err != nil {
		return filepath.Join(os.TempDir(), "cliamp.sock")
	}
	return filepath.Join(dir, "cliamp.sock")
}

// Send connects to the IPC socket, sends a request, and returns the response.
// The connection is closed after a single request/response exchange.
func Send(sockPath string, req Request) (Response, error) {
	return SendWithDeadline(sockPath, req, 5*time.Second)
}

// SendWithDeadline is like Send but lets the caller override the exchange
// deadline. Plugin commands can legitimately run for minutes (downloads), so
// the generic 5s cap is too short for them.
func SendWithDeadline(sockPath string, req Request, deadline time.Duration) (Response, error) {
	conn, err := dialSocket(sockPath, 3*time.Second)
	if err != nil {
		if isSocketUnavailable(err) {
			return Response{}, fmt.Errorf("cliamp is not running (no socket at %s)", sockPath)
		}
		return Response{}, fmt.Errorf("connect: %w", err)
	}
	defer conn.Close()

	conn.SetDeadline(time.Now().Add(deadline))

	// Encode and send the request as a single JSON line.
	data, err := json.Marshal(req)
	if err != nil {
		return Response{}, fmt.Errorf("marshal request: %w", err)
	}
	data = append(data, '\n')
	if _, err := conn.Write(data); err != nil {
		return Response{}, fmt.Errorf("write: %w", err)
	}

	// Read the response line.
	scanner := bufio.NewScanner(conn)
	if !scanner.Scan() {
		if err := scanner.Err(); err != nil {
			return Response{}, fmt.Errorf("read response: %w", err)
		}
		return Response{}, fmt.Errorf("no response from server")
	}

	var resp Response
	if err := json.Unmarshal(scanner.Bytes(), &resp); err != nil {
		return Response{}, fmt.Errorf("unmarshal response: %w", err)
	}
	return resp, nil
}
