package action

import (
	"context"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

// AnalyzeCodeInput is input for AnalyzeCodeActivity
type AnalyzeCodeInput struct {
	Path     string `json:"path"`
	Language string `json:"language,omitempty"`
	Depth    int    `json:"depth,omitempty"`
}

// AnalyzeCodeOutput is output from AnalyzeCodeActivity
type AnalyzeCodeOutput struct {
	Quality  float64           `json:"quality"`
	Metrics  map[string]interface{} `json:"metrics"`
	Issues   []string          `json:"issues"`
	Summary  string            `json:"summary"`
}

// AnalyzeCodeActivity analyzes code quality using available tools
func AnalyzeCodeActivity(ctx context.Context, in AnalyzeCodeInput) (AnalyzeCodeOutput, error) {
	logger := newActivityLogger(ctx)
	logger.Info("AnalyzeCodeActivity started", "path", in.Path)

	output := AnalyzeCodeOutput{
		Quality: 0.0,
		Metrics: make(map[string]interface{}),
		Issues:  []string{},
	}

	// Verify path exists
	if _, err := os.Stat(in.Path); os.IsNotExist(err) {
		return output, fmt.Errorf("path does not exist: %s", in.Path)
	}

	// Count files and lines
	var totalFiles, totalLines int
	err := filepath.Walk(in.Path, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return nil // Skip errors
		}
		if info.IsDir() {
			// Skip hidden and vendor directories
			if strings.HasPrefix(info.Name(), ".") || info.Name() == "vendor" || info.Name() == "node_modules" {
				return filepath.SkipDir
			}
			return nil
		}
		
		ext := filepath.Ext(path)
		if isCodeFile(ext) {
			totalFiles++
			if lines, err := countLines(path); err == nil {
				totalLines += lines
			}
		}
		return nil
	})
	if err != nil {
		logger.Warn("Error walking path", "error", err)
	}

	output.Metrics["totalFiles"] = totalFiles
	output.Metrics["totalLines"] = totalLines

	// Try to run go vet if it's a Go project
	if _, err := os.Stat(filepath.Join(in.Path, "go.mod")); err == nil {
		cmd := exec.CommandContext(ctx, "go", "vet", "./...")
		cmd.Dir = in.Path
		vetOutput, err := cmd.CombinedOutput()
		if err != nil {
			issues := strings.Split(string(vetOutput), "\n")
			for _, issue := range issues {
				if strings.TrimSpace(issue) != "" {
					output.Issues = append(output.Issues, issue)
				}
			}
		}
		output.Metrics["goVetRan"] = true
	}

	// Calculate quality score (simple heuristic)
	issueCount := len(output.Issues)
	if totalFiles > 0 {
		issuesPerFile := float64(issueCount) / float64(totalFiles)
		output.Quality = max(0, 1.0 - (issuesPerFile * 0.1))
	} else {
		output.Quality = 0.5
	}

	output.Summary = fmt.Sprintf("Analyzed %d files (%d lines). Found %d issues. Quality score: %.2f",
		totalFiles, totalLines, issueCount, output.Quality)

	logger.Info("AnalyzeCodeActivity completed", "quality", output.Quality, "issues", issueCount)
	return output, nil
}

// SecurityScanInput is input for SecurityScanActivity
type SecurityScanInput struct {
	Path     string `json:"path"`
	Severity string `json:"severity,omitempty"` // low, medium, high, critical
}

// SecurityScanOutput is output from SecurityScanActivity
type SecurityScanOutput struct {
	Vulnerabilities []Vulnerability `json:"vulnerabilities"`
	SecurityScore   float64         `json:"securityScore"`
	RiskLevel       string          `json:"riskLevel"`
}

// Vulnerability represents a security issue
type Vulnerability struct {
	ID          string `json:"id"`
	Severity    string `json:"severity"`
	Description string `json:"description"`
	File        string `json:"file,omitempty"`
	Line        int    `json:"line,omitempty"`
}

// SecurityScanActivity scans code for security vulnerabilities
func SecurityScanActivity(ctx context.Context, in SecurityScanInput) (SecurityScanOutput, error) {
	logger := newActivityLogger(ctx)
	logger.Info("SecurityScanActivity started", "path", in.Path)

	output := SecurityScanOutput{
		Vulnerabilities: []Vulnerability{},
		SecurityScore:   100.0,
		RiskLevel:       "low",
	}

	// Verify path exists
	if _, err := os.Stat(in.Path); os.IsNotExist(err) {
		return output, fmt.Errorf("path does not exist: %s", in.Path)
	}

	// Check for common security issues
	// 1. Check for hardcoded secrets
	secretPatterns := []string{
		"password=",
		"secret=",
		"api_key=",
		"apikey=",
		"private_key",
		"AWS_SECRET",
	}

	err := filepath.Walk(in.Path, func(path string, info os.FileInfo, err error) error {
		if err != nil || info.IsDir() {
			return nil
		}
		
		if !isCodeFile(filepath.Ext(path)) {
			return nil
		}

		content, err := os.ReadFile(path)
		if err != nil {
			return nil
		}

		contentStr := strings.ToLower(string(content))
		for _, pattern := range secretPatterns {
			if strings.Contains(contentStr, pattern) {
				output.Vulnerabilities = append(output.Vulnerabilities, Vulnerability{
					ID:          fmt.Sprintf("SEC-%d", len(output.Vulnerabilities)+1),
					Severity:    "high",
					Description: fmt.Sprintf("Possible hardcoded secret: %s", pattern),
					File:        path,
				})
			}
		}
		return nil
	})
	if err != nil {
		logger.Warn("Error scanning", "error", err)
	}

	// Try gosec if available and it's a Go project
	if _, err := os.Stat(filepath.Join(in.Path, "go.mod")); err == nil {
		if _, err := exec.LookPath("gosec"); err == nil {
			cmd := exec.CommandContext(ctx, "gosec", "-fmt=json", "-quiet", "./...")
			cmd.Dir = in.Path
			// gosec returns non-zero if issues found, so we ignore the error
			cmd.CombinedOutput()
		}
	}

	// Calculate score
	vulnCount := len(output.Vulnerabilities)
	if vulnCount == 0 {
		output.SecurityScore = 100.0
		output.RiskLevel = "low"
	} else if vulnCount < 3 {
		output.SecurityScore = 80.0
		output.RiskLevel = "medium"
	} else if vulnCount < 10 {
		output.SecurityScore = 50.0
		output.RiskLevel = "high"
	} else {
		output.SecurityScore = 20.0
		output.RiskLevel = "critical"
	}

	logger.Info("SecurityScanActivity completed", "vulnerabilities", vulnCount, "riskLevel", output.RiskLevel)
	return output, nil
}

// GenerateReportInput is input for GenerateReportActivity
type GenerateReportInput struct {
	AnalysisResult interface{} `json:"analysisResult"`
	SecurityResult interface{} `json:"securityResult"`
	Format         string      `json:"format,omitempty"` // markdown, html, json
}

// GenerateReportOutput is output from GenerateReportActivity
type GenerateReportOutput struct {
	Report     string `json:"report"`
	ReportPath string `json:"reportPath"`
}

// GenerateReportActivity generates a combined report
func GenerateReportActivity(ctx context.Context, in GenerateReportInput) (GenerateReportOutput, error) {
	logger := newActivityLogger(ctx)
	logger.Info("GenerateReportActivity started")

	format := in.Format
	if format == "" {
		format = "markdown"
	}

	var report strings.Builder
	timestamp := time.Now().Format(time.RFC3339)

	switch format {
	case "markdown":
		report.WriteString("# Analysis Report\n\n")
		report.WriteString(fmt.Sprintf("Generated: %s\n\n", timestamp))
		report.WriteString("## Code Analysis\n\n")
		report.WriteString(fmt.Sprintf("```\n%v\n```\n\n", in.AnalysisResult))
		report.WriteString("## Security Scan\n\n")
		report.WriteString(fmt.Sprintf("```\n%v\n```\n\n", in.SecurityResult))
	case "json":
		report.WriteString(fmt.Sprintf(`{"timestamp":"%s","analysis":%v,"security":%v}`,
			timestamp, in.AnalysisResult, in.SecurityResult))
	default:
		report.WriteString(fmt.Sprintf("Report generated at %s\n", timestamp))
		report.WriteString(fmt.Sprintf("Analysis: %v\n", in.AnalysisResult))
		report.WriteString(fmt.Sprintf("Security: %v\n", in.SecurityResult))
	}

	// Save to temp file
	reportPath := filepath.Join(os.TempDir(), fmt.Sprintf("report-%d.%s", time.Now().UnixNano(), format))
	if err := os.WriteFile(reportPath, []byte(report.String()), 0644); err != nil {
		logger.Warn("Failed to save report", "error", err)
	}

	logger.Info("GenerateReportActivity completed", "format", format)
	return GenerateReportOutput{
		Report:     report.String(),
		ReportPath: reportPath,
	}, nil
}

// Helper functions

func isCodeFile(ext string) bool {
	codeExts := map[string]bool{
		".go": true, ".py": true, ".js": true, ".ts": true,
		".java": true, ".c": true, ".cpp": true, ".h": true,
		".rs": true, ".rb": true, ".php": true, ".swift": true,
	}
	return codeExts[ext]
}

func countLines(path string) (int, error) {
	content, err := os.ReadFile(path)
	if err != nil {
		return 0, err
	}
	return len(strings.Split(string(content), "\n")), nil
}

func max(a, b float64) float64 {
	if a > b {
		return a
	}
	return b
}
