package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/CalmoAI/mcp-server-signoz/internal/application/service"
	"github.com/CalmoAI/mcp-server-signoz/internal/domain"
	"github.com/CalmoAI/mcp-server-signoz/internal/infrastructure/config"
	"github.com/CalmoAI/mcp-server-signoz/internal/infrastructure/signoz"
	"github.com/CalmoAI/mcp-server-signoz/pkg/mcp/server"
	"github.com/CalmoAI/mcp-server-signoz/pkg/mcp/transport"
)

// TransportMode represents the transport mode for the MCP server
type TransportMode string

const (
	TransportModeStdio TransportMode = "stdio"
	// Future transport modes can be added here:
	// TransportModeHTTP TransportMode = "http"
	// TransportModeTCP  TransportMode = "tcp"
)

// detectTransportMode determines the transport mode from command line arguments
func detectTransportMode() TransportMode {
	// Parse command line arguments manually to avoid interfering with flag package
	// if it's used elsewhere in the future
	args := os.Args[1:] // Skip program name

	// Check for explicit stdio argument
	for _, arg := range args {
		if arg == "stdio" {
			return TransportModeStdio
		}
	}

	// Default to stdio for MCP servers (most common case)
	return TransportModeStdio
}

// isStdioMode returns true if the server is running in stdio mode
func isStdioMode(mode TransportMode) bool {
	return mode == TransportModeStdio
}

func main() {
	// Detect transport mode early
	transportMode := detectTransportMode()
	// Load configuration
	configRepo := config.NewRepository()
	if err := configRepo.Load(); err != nil {
		log.Fatalf("Failed to load configuration: %v", err)
	}

	// Initialize dependencies with transport mode awareness
	deps, err := initializeDependencies(configRepo, transportMode)
	if err != nil {
		log.Fatalf("Failed to initialize dependencies: %v", err)
	}
	defer deps.cleanup()

	// Create MCP service
	mcpService := service.NewMCPService(
		deps.signozClient,
		deps.logger,
		deps.tracer,
		deps.metrics,
		deps.cache,
	)

	// Create MCP server
	mcpServer := server.NewServer(
		deps.transport,
		mcpService,
		deps.logger,
		deps.tracer,
		deps.metrics,
	)

	// Set up graceful shutdown
	_, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Handle shutdown signals
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		<-sigChan
		deps.logger.Info("received shutdown signal")
		cancel()

		if err := mcpServer.Shutdown(); err != nil {
			deps.logger.Error("error shutting down MCP server",
				domain.Field{Key: "error", Value: err.Error()})
		}
	}()

	deps.logger.Info("starting Signoz MCP server",
		domain.Field{Key: "version", Value: "1.0.0"},
		domain.Field{Key: "signoz_url", Value: configRepo.GetSignozURL()},
	)

	// Run the server
	if err := mcpServer.Run(); err != nil && err != context.Canceled {
		deps.logger.Error("MCP server error",
			domain.Field{Key: "error", Value: err.Error()})
		os.Exit(1)
	}

	deps.logger.Info("Signoz MCP server stopped")
}

// dependencies holds all initialized dependencies
type dependencies struct {
	configRepo    *config.Repository
	signozClient  *signoz.Client
	logger        StdioAwareLogger
	tracer        NoOpTracer
	metrics       NoOpMetrics
	cache         NoOpCache
	transport     *transport.StdioTransport
	transportMode TransportMode
}

func (d *dependencies) cleanup() {
	if d.transport != nil {
		d.transport.Close()
	}
}

func (d *dependencies) createField(key string, value interface{}) SimpleField {
	return SimpleField{Key: key, Value: value}
}

// initializeDependencies initializes all application dependencies
func initializeDependencies(configRepo *config.Repository, transportMode TransportMode) (*dependencies, error) {
	deps := &dependencies{
		configRepo:    configRepo,
		transportMode: transportMode,
	}

	// Initialize stdio-aware logger
	deps.logger = NewStdioAwareLogger(configRepo.GetLogConfig().Level, transportMode)

	// Initialize tracer (no-op for now)
	deps.tracer = NoOpTracer{}

	// Initialize metrics (no-op for now)
	deps.metrics = NoOpMetrics{}

	// Initialize cache (no-op for now)
	deps.cache = NoOpCache{}

	// Initialize Signoz client
	signozClient, err := signoz.NewClient(configRepo.GetSignozConfig())
	if err != nil {
		return nil, fmt.Errorf("failed to create Signoz client: %w", err)
	}
	deps.signozClient = signozClient

	// Initialize transport
	deps.transport = transport.NewStdioTransport()

	return deps, nil
}

// Simple implementations for the interfaces to get the server running

// StdioAwareLogger implements a logger that adapts its behavior based on transport mode
type StdioAwareLogger struct {
	level         string
	transportMode TransportMode
	forceStderr   bool
}

// SimpleField represents a log field
type SimpleField struct {
	Key   string
	Value interface{}
}

// NewStdioAwareLogger creates a new stdio-aware logger
func NewStdioAwareLogger(level string, transportMode TransportMode) StdioAwareLogger {
	return StdioAwareLogger{
		level:         strings.ToLower(level),
		transportMode: transportMode,
		forceStderr:   isStdioMode(transportMode), // Force stderr when in stdio mode
	}
}

func (l StdioAwareLogger) shouldLog(level string) bool {
	levels := map[string]int{
		"debug": 0,
		"info":  1,
		"warn":  2,
		"error": 3,
		"fatal": 4,
	}

	currentLevel := levels[l.level]
	targetLevel := levels[level]

	return targetLevel >= currentLevel
}

func (l StdioAwareLogger) log(level string, msg string, fields ...SimpleField) {
	if !l.shouldLog(level) {
		return
	}

	timestamp := time.Now().Format(time.RFC3339)

	// Always write to stderr when in stdio mode to avoid interfering with MCP protocol
	// For other transport modes, could potentially use different outputs
	if l.forceStderr || l.transportMode == TransportModeStdio {
		fmt.Fprintf(os.Stderr, "[%s] %s: %s", timestamp, strings.ToUpper(level), msg)
		for _, field := range fields {
			fmt.Fprintf(os.Stderr, " %s=%v", field.Key, field.Value)
		}
		fmt.Fprintln(os.Stderr)
	} else {
		// Future: could route to different outputs for other transport modes
		// For now, default to stderr for safety
		fmt.Fprintf(os.Stderr, "[%s] %s: %s", timestamp, strings.ToUpper(level), msg)
		for _, field := range fields {
			fmt.Fprintf(os.Stderr, " %s=%v", field.Key, field.Value)
		}
		fmt.Fprintln(os.Stderr)
	}
}

func (l StdioAwareLogger) Debug(msg string, fields ...domain.Field) {
	simpleFields := make([]SimpleField, len(fields))
	for i, f := range fields {
		simpleFields[i] = SimpleField{Key: f.Key, Value: f.Value}
	}
	l.log("debug", msg, simpleFields...)
}

func (l StdioAwareLogger) Info(msg string, fields ...domain.Field) {
	simpleFields := make([]SimpleField, len(fields))
	for i, f := range fields {
		simpleFields[i] = SimpleField{Key: f.Key, Value: f.Value}
	}
	l.log("info", msg, simpleFields...)
}

func (l StdioAwareLogger) Warn(msg string, fields ...domain.Field) {
	simpleFields := make([]SimpleField, len(fields))
	for i, f := range fields {
		simpleFields[i] = SimpleField{Key: f.Key, Value: f.Value}
	}
	l.log("warn", msg, simpleFields...)
}

func (l StdioAwareLogger) Error(msg string, fields ...domain.Field) {
	simpleFields := make([]SimpleField, len(fields))
	for i, f := range fields {
		simpleFields[i] = SimpleField{Key: f.Key, Value: f.Value}
	}
	l.log("error", msg, simpleFields...)
}

func (l StdioAwareLogger) Fatal(msg string, fields ...domain.Field) {
	simpleFields := make([]SimpleField, len(fields))
	for i, f := range fields {
		simpleFields[i] = SimpleField{Key: f.Key, Value: f.Value}
	}
	l.log("fatal", msg, simpleFields...)
	os.Exit(1)
}

func (l StdioAwareLogger) With(fields ...domain.Field) domain.Logger {
	return l // For simplicity, return self
}

// GetTransportMode returns the current transport mode
func (l StdioAwareLogger) GetTransportMode() TransportMode {
	return l.transportMode
}

// IsStdioMode returns true if the logger is in stdio mode
func (l StdioAwareLogger) IsStdioMode() bool {
	return l.forceStderr
}

// NoOpTracer implements a no-op tracer
type NoOpTracer struct{}

func (NoOpTracer) StartSpan(ctx context.Context, name string, opts ...domain.SpanOption) (context.Context, domain.Span) {
	return ctx, NoOpSpan{}
}

// NoOpSpan implements a no-op span
type NoOpSpan struct{}

func (NoOpSpan) SetAttribute(key string, value interface{})           {}
func (NoOpSpan) SetStatus(code domain.StatusCode, description string) {}
func (NoOpSpan) RecordError(err error, opts ...domain.EventOption)    {}
func (NoOpSpan) End()                                                 {}

// NoOpMetrics implements a no-op metrics
type NoOpMetrics struct{}

func (NoOpMetrics) Counter(name string) domain.Counter {
	return NoOpCounter{}
}

func (NoOpMetrics) Histogram(name string) domain.Histogram {
	return NoOpHistogram{}
}

func (NoOpMetrics) Gauge(name string) domain.Gauge {
	return NoOpGauge{}
}

// NoOpCounter implements a no-op counter
type NoOpCounter struct{}

func (NoOpCounter) Add(ctx context.Context, value int64, attributes ...domain.Attribute) {}

// NoOpHistogram implements a no-op histogram
type NoOpHistogram struct{}

func (NoOpHistogram) Record(ctx context.Context, value float64, attributes ...domain.Attribute) {}

// NoOpGauge implements a no-op gauge
type NoOpGauge struct{}

func (NoOpGauge) Set(ctx context.Context, value float64, attributes ...domain.Attribute) {}

// NoOpCache implements a no-op cache
type NoOpCache struct{}

func (NoOpCache) Get(ctx context.Context, key string) (interface{}, error) {
	return nil, fmt.Errorf("cache miss")
}

func (NoOpCache) Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error {
	return nil
}

func (NoOpCache) Delete(ctx context.Context, key string) error {
	return nil
}

func (NoOpCache) Clear(ctx context.Context) error {
	return nil
}
