package nodejs

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

type NodeJS struct {
	// Add fields as needed for Node.js analysis
}

// Name returns the name of the Node.js technology stack.
func (n *NodeJS) Name() string {
	return "Node.js"
}

// Analyze analyzes the codebase and returns a guessed technology stack for Node.js.
func (n *NodeJS) Analyze(codebasePath string) (bool, error) {
	// Implement the logic to analyze the Node.js codebase
	// This could involve checking for specific files, directories, or patterns
	// For example, checking for a Dockerfile, package.json, server.js, etc.
	// Return true if the analysis confirms it's a Node.js project

	// Placeholder implementation - adapt this to your actual analysis logic
	// For instance, check for package.json
	if _, err := os.Stat(filepath.Join(codebasePath, "package.json")); err == nil {
		return true, nil // It's a Node.js project
	}
	return false, nil // Not a Node.js project or error (handle error appropriately)
}

type NodeProjectAnalysis struct {
	NodeVersion     string   `json:"node_version"`
	PackageManager  string   `json:"package_manager"`
	Entrypoint      string   `json:"entrypoint"`
	Framework       string   `json:"framework"`
	Port            int      `json:"port"`
	BuildScript     string   `json:"build_script"`
	StartScript     string   `json:"start_script"`
	Dependencies    []string `json:"dependencies"`
	DevDependencies []string `json:"dev_dependencies"`
}

func (n *NodeJS) analyzeNodeProject(codebasePath string) (*NodeProjectAnalysis, error) {
	analysis := &NodeProjectAnalysis{
		Port: 3000, // Default for Node.js
	}

	// Read package.json
	packageJsonPath := filepath.Join(codebasePath, "package.json")
	content, err := os.ReadFile(packageJsonPath)
	if err != nil {
		return nil, fmt.Errorf("package.json not found: %w", err)
	}

	var packageJson struct {
		Main            string            `json:"main"`
		Scripts         map[string]string `json:"scripts"`
		Dependencies    map[string]string `json:"dependencies"`
		DevDependencies map[string]string `json:"devDependencies"`
		Engines         map[string]string `json:"engines"`
	}

	if err := json.Unmarshal(content, &packageJson); err != nil {
		return nil, fmt.Errorf("failed to parse package.json: %w", err)
	}

	// Determine entrypoint
	analysis.Entrypoint = packageJson.Main
	if analysis.Entrypoint == "" {
		// Check common entry points
		entrypoints := []string{"server.js", "app.js", "index.js", "main.js", "src/index.js", "src/server.js"}
		for _, ep := range entrypoints {
			if n.fileExists(filepath.Join(codebasePath, ep)) {
				analysis.Entrypoint = ep
				break
			}
		}
		if analysis.Entrypoint == "" {
			analysis.Entrypoint = "index.js" // Default
		}
	}

	// Detect package manager
	if n.fileExists(filepath.Join(codebasePath, "yarn.lock")) {
		analysis.PackageManager = "yarn"
	} else if n.fileExists(filepath.Join(codebasePath, "pnpm-lock.yaml")) {
		analysis.PackageManager = "pnpm"
	} else {
		analysis.PackageManager = "npm"
	}

	// Get Node version from engines
	if nodeVersion, ok := packageJson.Engines["node"]; ok {
		// Extract version number (remove ^ and ~ prefixes)
		analysis.NodeVersion = strings.TrimLeft(nodeVersion, "^~>=")
		if strings.Contains(analysis.NodeVersion, ".") {
			parts := strings.Split(analysis.NodeVersion, ".")
			analysis.NodeVersion = parts[0] // Use major version only
		}
	}

	// Detect scripts
	if buildScript, ok := packageJson.Scripts["build"]; ok && buildScript != "" {
		analysis.BuildScript = "build"
	}
	if startScript, ok := packageJson.Scripts["start"]; ok && startScript != "" {
		analysis.StartScript = "start"
	}

	// Detect framework from dependencies
	for dep := range packageJson.Dependencies {
		switch {
		case strings.Contains(dep, "express"):
			analysis.Framework = "express"
		case strings.Contains(dep, "next"):
			analysis.Framework = "nextjs"
		case strings.Contains(dep, "react"):
			analysis.Framework = "react"
		case strings.Contains(dep, "vue"):
			analysis.Framework = "vue"
		case strings.Contains(dep, "angular"):
			analysis.Framework = "angular"
		case strings.Contains(dep, "fastify"):
			analysis.Framework = "fastify"
		case strings.Contains(dep, "koa"):
			analysis.Framework = "koa"
		}
	}

	// Extract dependency names
	for dep := range packageJson.Dependencies {
		analysis.Dependencies = append(analysis.Dependencies, dep)
	}

	return analysis, nil
}

func (n *NodeJS) fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

// GenerateDockerfile generates a Dockerfile for Node.js projects based on analysis
func (n *NodeJS) GenerateDockerfile(codebasePath string) (string, error) {
	// Analyze the Node.js project
	analysis, err := n.analyzeNodeProject(codebasePath)
	if err != nil {
		return "", fmt.Errorf("failed to analyze Node.js project: %w", err)
	}

	nodeVersion := analysis.NodeVersion
	if nodeVersion == "" {
		nodeVersion = "18"
	}

	// Determine install command based on package manager
	var installCmd string
	var copyLockFile string
	switch analysis.PackageManager {
	case "yarn":
		installCmd = "yarn install --frozen-lockfile --production"
		copyLockFile = "COPY yarn.lock ./yarn.lock"
	case "pnpm":
		installCmd = "pnpm install --frozen-lockfile --prod"
		copyLockFile = "COPY pnpm-lock.yaml ./pnpm-lock.yaml"
	default:
		installCmd = "npm ci --only=production"
		copyLockFile = "COPY package-lock.json ./package-lock.json"
	}

	// Add build script if exists
	buildScript := ""
	if analysis.BuildScript != "" {
		switch analysis.PackageManager {
		case "yarn":
			buildScript = "RUN yarn build"
		case "pnpm":
			buildScript = "RUN pnpm build"
		default:
			buildScript = "RUN npm run build"
		}
	}

	dockerfile := fmt.Sprintf(`# Auto-generated Dockerfile for Node.js application
# Generated by BYOP Engine - Node.js Stack Analyzer

FROM node:%s-alpine

# Set working directory
WORKDIR /app

# Copy package files for better caching
COPY package.json ./
%s

# Install dependencies
RUN %s

# Copy source code
COPY . .

# Build the application if build script exists
%s

# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nextjs -u 1001

# Change ownership
RUN chown -R nextjs:nodejs /app
USER nextjs

# Expose port
EXPOSE %d

# Start the application
CMD ["%s"]
`,
		nodeVersion,
		copyLockFile,
		installCmd,
		buildScript,
		analysis.Port,
		analysis.StartScript,
	)

	return dockerfile, nil
}
