// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

// Package mcp provides the MCP server command for lune.
package mcp

import (
	"errors"
	"fmt"

	"git.secluded.site/lune/internal/client"
	"git.secluded.site/lune/internal/config"
	"github.com/spf13/cobra"
)

// Transport constants.
const (
	TransportStdio = "stdio"
	TransportSSE   = "sse"
	TransportHTTP  = "http"
)

var (
	errUnknownTransport = errors.New("unknown transport; use stdio, sse, or http")
	errNoToken          = errors.New("no access token; run 'lune init' first")
)

var (
	transport string
	host      string
	port      int
)

// Cmd is the mcp command for starting the MCP server.
var Cmd = &cobra.Command{
	Use:   "mcp",
	Short: "Start the MCP server",
	Long: `Start a Model Context Protocol server for LLM tool integration.

The MCP server exposes Lunatask resources and tools that can be used by
LLM assistants (like Claude) to interact with your Lunatask data.

Transports:
  stdio  - Standard input/output (default, for local integrations)
  sse    - Server-sent events over HTTP
  http   - Streamable HTTP

Examples:
  lune mcp                    # Start with stdio (default)
  lune mcp -t sse             # Start SSE server on configured host:port
  lune mcp -t sse --port 9000 # Override port`,
	RunE: runMCP,
}

func init() {
	Cmd.Flags().StringVarP(&transport, "transport", "t", "",
		"Transport type: stdio, sse, http (default: stdio or config)")
	Cmd.Flags().StringVar(&host, "host", "", "Server host (for sse/http)")
	Cmd.Flags().IntVar(&port, "port", 0, "Server port (for sse/http)")
}

func runMCP(cmd *cobra.Command, _ []string) error {
	cfg, err := loadConfig()
	if err != nil {
		return err
	}

	token, err := client.GetToken()
	if err != nil {
		return fmt.Errorf("getting access token: %w", err)
	}

	if token == "" {
		return errNoToken
	}

	mcpServer := newMCPServer(cfg, token)

	effectiveTransport := resolveTransport(cfg)

	switch effectiveTransport {
	case TransportStdio:
		return runStdio(mcpServer)
	case TransportSSE:
		return runSSE(cmd, mcpServer, cfg)
	case TransportHTTP:
		return runHTTP(cmd, mcpServer, cfg)
	default:
		return errUnknownTransport
	}
}

func loadConfig() (*config.Config, error) {
	cfg, err := config.Load()
	if err != nil {
		if errors.Is(err, config.ErrNotFound) {
			cfg = &config.Config{}
		} else {
			return nil, fmt.Errorf("loading config: %w", err)
		}
	}

	cfg.MCP.MCPDefaults()

	return cfg, nil
}

func resolveTransport(_ *config.Config) string {
	if transport != "" {
		return transport
	}

	return TransportStdio
}

func resolveHost(cfg *config.Config) string {
	if host != "" {
		return host
	}

	return cfg.MCP.Host
}

func resolvePort(cfg *config.Config) int {
	if port != 0 {
		return port
	}

	return cfg.MCP.Port
}
