package services

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"text/template"
	"time"
)

// AgentBuilder handles generating embedded agent configurations
// Deprecated: Configuration logic should use services.ConfigService
type AgentBuilder struct {
	buildContext string
}

// NewAgentBuilder creates a new agent builder
func NewAgentBuilder() *AgentBuilder {
	return &AgentBuilder{}
}

// BuildAgentWithConfig generates agent configuration and prepares signed binary
// Deprecated: Delegate config generation to services.ConfigService
func (ab *AgentBuilder) BuildAgentWithConfig(config *AgentConfiguration) (*BuildResult, error) {
	// Create temporary build directory
	buildDir, err := os.MkdirTemp("", "agent-build-")
	if err != nil {
		return nil, fmt.Errorf("failed to create build directory: %w", err)
	}

	// Generate config.json (not embedded in binary)
	configJSONPath := filepath.Join(buildDir, "config.json")
	configJSON, err := ab.generateConfigJSON(config)
	if err != nil {
		os.RemoveAll(buildDir)
		return nil, fmt.Errorf("failed to generate config JSON: %w", err)
	}

	// Write config.json to file
	if err := os.WriteFile(configJSONPath, []byte(configJSON), 0600); err != nil {
		os.RemoveAll(buildDir)
		return nil, fmt.Errorf("failed to write config file: %w", err)
	}

	// Note: Binary is pre-built and stored in /app/binaries/{platform}/
	// We don't build or modify the binary here - it's generic for all agents
	// The signing happens at the platform level, not per-agent

	return &BuildResult{
		BuildDir:     buildDir,
		AgentID:      config.AgentID,
		ConfigFile:   configJSONPath,
		ConfigJSON:   configJSON,
		Platform:     config.Platform,
		BuildTime:    time.Now(),
	}, nil
}

// generateConfigJSON converts configuration to JSON format
func (ab *AgentBuilder) generateConfigJSON(config *AgentConfiguration) (string, error) {
	// Create complete configuration
	completeConfig := make(map[string]interface{})

	// Copy public configuration
	for k, v := range config.PublicConfig {
		completeConfig[k] = v
	}

	// Add secrets (they will be protected by file permissions at runtime)
	for k, v := range config.Secrets {
		completeConfig[k] = v
	}

	// CRITICAL: Add both version fields explicitly
	// These MUST be present or middleware will block the agent
	completeConfig["version"] = config.ConfigVersion       // Config schema version (e.g., "5")
	completeConfig["agent_version"] = config.AgentVersion  // Agent binary version (e.g., "0.1.23.6")

	// Add agent metadata
	completeConfig["agent_id"] = config.AgentID
	completeConfig["server_url"] = config.ServerURL
	completeConfig["organization"] = config.Organization
	completeConfig["environment"] = config.Environment
	completeConfig["template"] = config.Template
	completeConfig["build_time"] = config.BuildTime.Format(time.RFC3339)

	// Convert to JSON
	jsonBytes, err := json.MarshalIndent(completeConfig, "", "  ")
	if err != nil {
		return "", fmt.Errorf("failed to marshal config to JSON: %w", err)
	}

	return string(jsonBytes), nil
}

// BuildResult contains the results of the build process
type BuildResult struct {
	BuildDir    string    `json:"build_dir"`
	AgentID     string    `json:"agent_id"`
	ConfigFile  string    `json:"config_file"`
	ConfigJSON  string    `json:"config_json"`
	Platform    string    `json:"platform"`
	BuildTime   time.Time `json:"build_time"`
}

// generateEmbeddedConfig generates the embedded configuration Go file
func (ab *AgentBuilder) generateEmbeddedConfig(filename string, config *AgentConfiguration) error {
	// Create directory structure
	if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
		return err
	}

	// Convert configuration to JSON for embedding
	configJSON, err := ab.configToJSON(config)
	if err != nil {
		return err
	}

	// Generate Go source file with embedded configuration
	tmpl := `// Code generated by dynamic build system. DO NOT EDIT.
package embedded

import (
	"encoding/json"
	"time"
)

// EmbeddedAgentConfiguration contains the pre-built agent configuration
var EmbeddedAgentConfiguration = []byte(` + "`" + `{{.ConfigJSON}}` + "`" + `)

// EmbeddedAgentID contains the agent ID
var EmbeddedAgentID = "{{.AgentID}}"

// EmbeddedServerURL contains the server URL
var EmbeddedServerURL = "{{.ServerURL}}"

// EmbeddedOrganization contains the organization
var EmbeddedOrganization = "{{.Organization}}"

// EmbeddedEnvironment contains the environment
var EmbeddedEnvironment = "{{.Environment}}"

// EmbeddedTemplate contains the template type
var EmbeddedTemplate = "{{.Template}}"

// EmbeddedBuildTime contains the build time
var EmbeddedBuildTime, _ = time.Parse(time.RFC3339, "{{.BuildTime}}")

// GetEmbeddedConfig returns the embedded configuration as a map
func GetEmbeddedConfig() (map[string]interface{}, error) {
	var config map[string]interface{}
	err := json.Unmarshal(EmbeddedAgentConfiguration, &config)
	return config, err
}

// SecretsMapping maps configuration fields to Docker secrets
var SecretsMapping = map[string]string{
	{{range $key, $value := .Secrets}}"{{$key}}": "{{$value}}",
	{{end}}
}
`

	// Execute template
	t, err := template.New("embedded").Parse(tmpl)
	if err != nil {
		return fmt.Errorf("failed to parse template: %w", err)
	}

	file, err := os.Create(filename)
	if err != nil {
		return fmt.Errorf("failed to create file: %w", err)
	}
	defer file.Close()

	data := struct {
		ConfigJSON    string
		AgentID       string
		ServerURL     string
		Organization  string
		Environment   string
		Template      string
		BuildTime     string
		Secrets       map[string]string
	}{
		ConfigJSON:   configJSON,
		AgentID:      config.AgentID,
		ServerURL:    config.ServerURL,
		Organization: config.Organization,
		Environment:  config.Environment,
		Template:     config.Template,
		BuildTime:    config.BuildTime.Format(time.RFC3339),
		Secrets:      config.Secrets,
	}

	if err := t.Execute(file, data); err != nil {
		return fmt.Errorf("failed to execute template: %w", err)
	}

	return nil
}

// generateDockerCompose generates a docker-compose.yml file
func (ab *AgentBuilder) generateDockerCompose(filename string, config *AgentConfiguration) error {
	tmpl := `# Generated dynamically based on configuration
version: '3.8'

services:
  redflag-agent:
    image: {{.ImageTag}}
    container_name: redflag-agent-{{.AgentID}}
    restart: unless-stopped
    secrets:
      {{range $key := .SecretsKeys}}- {{$key}}
      {{end}}
    volumes:
      - /var/lib/redflag:/var/lib/redflag
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - REDFLAG_AGENT_ID={{.AgentID}}
      - REDFLAG_ENVIRONMENT={{.Environment}}
      - REDFLAG_SERVER_URL={{.ServerURL}}
      - REDFLAG_ORGANIZATION={{.Organization}}
    networks:
      - redflag
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

secrets:
  {{range $key, $value := .Secrets}}{{$key}}:
    external: true
  {{end}}

networks:
  redflag:
    external: true
`

	t, err := template.New("compose").Parse(tmpl)
	if err != nil {
		return err
	}

	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	// Extract secret keys for template
	secretsKeys := make([]string, 0, len(config.Secrets))
	for key := range config.Secrets {
		secretsKeys = append(secretsKeys, key)
	}

	data := struct {
		ImageTag   string
		AgentID    string
		Environment string
		ServerURL  string
		Organization string
		Secrets    map[string]string
		SecretsKeys []string
	}{
		ImageTag:     fmt.Sprintf("redflag-agent:%s", config.AgentID[:8]),
		AgentID:      config.AgentID,
		Environment:  config.Environment,
		ServerURL:    config.ServerURL,
		Organization: config.Organization,
		Secrets:      config.Secrets,
		SecretsKeys:  secretsKeys,
	}

	return t.Execute(file, data)
}

// generateDockerfile generates a Dockerfile for building the agent
func (ab *AgentBuilder) generateDockerfile(filename string, config *AgentConfiguration) error {
	tmpl := `# Dockerfile for RedFlag Agent with embedded configuration
FROM golang:1.21-alpine AS builder

# Install ca-certificates for SSL/TLS
RUN apk add --no-cache ca-certificates git

WORKDIR /app

# Copy go mod files (these should be in the same directory as the Dockerfile)
COPY go.mod go.sum ./
RUN go mod download

# Copy source code
COPY . .

# Copy generated embedded configuration
COPY pkg/embedded/config.go ./pkg/embedded/config.go

# Build the agent with embedded configuration
RUN CGO_ENABLED=0 GOOS=linux go build \
    -ldflags="-w -s -X main.version=dynamic-build-{{.AgentID}}" \
    -o redflag-agent \
    ./cmd/agent

# Final stage
FROM scratch

# Copy ca-certificates for SSL/TLS
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

# Copy the agent binary
COPY --from=builder /app/redflag-agent /redflag-agent

# Set environment variables (these can be overridden by docker-compose)
ENV REDFLAG_AGENT_ID="{{.AgentID}}"
ENV REDFLAG_ENVIRONMENT="{{.Environment}}"
ENV REDFLAG_SERVER_URL="{{.ServerURL}}"
ENV REDFLAG_ORGANIZATION="{{.Organization}}"

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
    CMD ["/redflag-agent", "--health-check"]

# Run the agent
ENTRYPOINT ["/redflag-agent"]
`

	t, err := template.New("dockerfile").Parse(tmpl)
	if err != nil {
		return err
	}

	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	data := struct {
		AgentID     string
		Environment string
		ServerURL   string
		Organization string
	}{
		AgentID:      config.AgentID,
		Environment:  config.Environment,
		ServerURL:    config.ServerURL,
		Organization: config.Organization,
	}

	return t.Execute(file, data)
}

// configToJSON converts configuration to JSON string
func (ab *AgentBuilder) configToJSON(config *AgentConfiguration) (string, error) {
	// Create complete configuration with embedded values
	completeConfig := make(map[string]interface{})

	// Copy public configuration
	for k, v := range config.PublicConfig {
		completeConfig[k] = v
	}

	// Add secrets values (they will be overridden by Docker secrets at runtime)
	for k, v := range config.Secrets {
		completeConfig[k] = v
	}

	// Convert to JSON with proper escaping
	jsonBytes, err := json.MarshalIndent(completeConfig, "", "  ")
	if err != nil {
		return "", fmt.Errorf("failed to marshal config to JSON: %w", err)
	}

	// Escape backticks for Go string literal
	jsonStr := string(jsonBytes)
	jsonStr = strings.ReplaceAll(jsonStr, "`", "` + \"`\" + `")

	return jsonStr, nil
}