// Package scanner manages security scanning tools
package scanner

import (
	"sync"
	"time"

	"github.com/google/uuid"
	"go.uber.org/zap"
)

// Manager orchestrates security scanning tools
type Manager struct {
	logger  *zap.Logger
	tools   map[string]*Tool
	scans   map[string]*Scan
	mu      sync.RWMutex
}

// Tool represents a security scanning tool
type Tool struct {
	Name        string `json:"name"`
	Version     string `json:"version"`
	Available   bool   `json:"available"`
	Description string `json:"description"`
	Category    string `json:"category"` // secrets, sast, container, dependencies, sbom
}

// Scan represents a security scan
type Scan struct {
	ID          string    `json:"id"`
	Status      string    `json:"status"` // PENDING, RUNNING, COMPLETED, FAILED
	Tools       []string  `json:"tools"`
	TargetPath  string    `json:"target_path"`
	StartedAt   time.Time `json:"started_at"`
	CompletedAt time.Time `json:"completed_at,omitempty"`
	Findings    []Finding `json:"findings"`
	Summary     Summary   `json:"summary"`
}

// Finding represents a security finding
type Finding struct {
	ID             string    `json:"id"`
	Tool           string    `json:"tool"`
	Severity       string    `json:"severity"` // CRITICAL, HIGH, MEDIUM, LOW
	Title          string    `json:"title"`
	Description    string    `json:"description"`
	File           string    `json:"file,omitempty"`
	Line           int       `json:"line,omitempty"`
	Recommendation string    `json:"recommendation"`
	Status         string    `json:"status"` // OPEN, IN_PROGRESS, RESOLVED, FALSE_POSITIVE
	CreatedAt      time.Time `json:"created_at"`
	CVE            string    `json:"cve,omitempty"`
}

// Summary represents scan summary
type Summary struct {
	Critical int `json:"critical"`
	High     int `json:"high"`
	Medium   int `json:"medium"`
	Low      int `json:"low"`
	Total    int `json:"total"`
}

// SBOM represents a Software Bill of Materials
type SBOM struct {
	ID           string      `json:"id"`
	Format       string      `json:"format"` // cyclonedx, spdx
	Version      string      `json:"version"`
	GeneratedAt  time.Time   `json:"generated_at"`
	Components   []Component `json:"components"`
	Dependencies []string    `json:"dependencies"`
}

// Component represents an SBOM component
type Component struct {
	Name            string   `json:"name"`
	Version         string   `json:"version"`
	Type            string   `json:"type"` // library, application, framework
	License         string   `json:"license"`
	Category        string   `json:"category"`
	Vulnerabilities []string `json:"vulnerabilities,omitempty"`
}

// NewManager creates a new scanner manager
func NewManager(logger *zap.Logger) *Manager {
	m := &Manager{
		logger: logger,
		tools:  make(map[string]*Tool),
		scans:  make(map[string]*Scan),
	}

	// Initialize tools
	m.initializeTools()

	return m
}

func (m *Manager) initializeTools() {
	m.tools = map[string]*Tool{
		"gitleaks": {
			Name:        "Gitleaks",
			Version:     "8.18.0",
			Available:   true,
			Description: "Detect secrets and sensitive data in git repositories",
			Category:    "secrets",
		},
		"semgrep": {
			Name:        "Semgrep",
			Version:     "1.51.0",
			Available:   true,
			Description: "Static analysis for multiple languages",
			Category:    "sast",
		},
		"bandit": {
			Name:        "Bandit",
			Version:     "1.7.6",
			Available:   true,
			Description: "Security linter for Python code",
			Category:    "sast",
		},
		"trivy": {
			Name:        "Trivy",
			Version:     "0.48.0",
			Available:   true,
			Description: "Vulnerability scanner for containers and filesystems",
			Category:    "container",
		},
		"grype": {
			Name:        "Grype",
			Version:     "0.73.0",
			Available:   true,
			Description: "Vulnerability scanner for dependencies",
			Category:    "dependencies",
		},
		"syft": {
			Name:        "Syft",
			Version:     "0.98.0",
			Available:   true,
			Description: "SBOM generator",
			Category:    "sbom",
		},
	}
}

// AvailableTools returns list of available tools
func (m *Manager) AvailableTools() []string {
	tools := make([]string, 0, len(m.tools))
	for name, tool := range m.tools {
		if tool.Available {
			tools = append(tools, name)
		}
	}
	return tools
}

// GetTools returns all tools with their status
func (m *Manager) GetTools() map[string]*Tool {
	return m.tools
}

// StartScan starts a new security scan
func (m *Manager) StartScan(tools []string, targetPath string) *Scan {
	scan := &Scan{
		ID:         uuid.New().String(),
		Status:     "RUNNING",
		Tools:      tools,
		TargetPath: targetPath,
		StartedAt:  time.Now(),
		Findings:   []Finding{},
	}

	m.mu.Lock()
	m.scans[scan.ID] = scan
	m.mu.Unlock()

	// Run scan asynchronously
	go m.runScan(scan)

	return scan
}

func (m *Manager) runScan(scan *Scan) {
	m.logger.Info("Starting scan", zap.String("id", scan.ID), zap.Strings("tools", scan.Tools))

	// Simulate scanning
	time.Sleep(3 * time.Second)

	// Generate sample findings
	findings := m.generateSampleFindings(scan.Tools)

	// Update scan
	m.mu.Lock()
	scan.Status = "COMPLETED"
	scan.CompletedAt = time.Now()
	scan.Findings = findings
	scan.Summary = m.calculateSummary(findings)
	m.mu.Unlock()

	m.logger.Info("Scan completed",
		zap.String("id", scan.ID),
		zap.Int("findings", len(findings)))
}

func (m *Manager) generateSampleFindings(tools []string) []Finding {
	findings := []Finding{}

	for _, tool := range tools {
		switch tool {
		case "gitleaks":
			// Usually no findings in clean repos
		case "trivy":
			findings = append(findings, Finding{
				ID:             uuid.New().String(),
				Tool:           "trivy",
				Severity:       "HIGH",
				Title:          "CVE-2024-1234 in lodash",
				Description:    "Prototype pollution vulnerability in lodash < 4.17.21",
				Recommendation: "Upgrade lodash to version 4.17.21 or higher",
				Status:         "OPEN",
				CreatedAt:      time.Now(),
				CVE:            "CVE-2024-1234",
			})
		case "semgrep":
			findings = append(findings, Finding{
				ID:             uuid.New().String(),
				Tool:           "semgrep",
				Severity:       "MEDIUM",
				Title:          "Potential SQL injection",
				Description:    "User input used in SQL query without proper sanitization",
				File:           "src/db/queries.ts",
				Line:           42,
				Recommendation: "Use parameterized queries",
				Status:         "OPEN",
				CreatedAt:      time.Now(),
			})
		case "grype":
			findings = append(findings, Finding{
				ID:             uuid.New().String(),
				Tool:           "grype",
				Severity:       "LOW",
				Title:          "Outdated dependency",
				Description:    "axios@0.21.0 has known vulnerabilities",
				Recommendation: "Update to axios@1.6.0 or higher",
				Status:         "OPEN",
				CreatedAt:      time.Now(),
			})
		}
	}

	return findings
}

func (m *Manager) calculateSummary(findings []Finding) Summary {
	summary := Summary{}
	for _, f := range findings {
		switch f.Severity {
		case "CRITICAL":
			summary.Critical++
		case "HIGH":
			summary.High++
		case "MEDIUM":
			summary.Medium++
		case "LOW":
			summary.Low++
		}
		summary.Total++
	}
	return summary
}

// GetScan returns a scan by ID
func (m *Manager) GetScan(scanID string) *Scan {
	m.mu.RLock()
	defer m.mu.RUnlock()
	return m.scans[scanID]
}

// GetAllFindings returns all findings across scans
func (m *Manager) GetAllFindings() []Finding {
	m.mu.RLock()
	defer m.mu.RUnlock()

	findings := []Finding{}
	for _, scan := range m.scans {
		findings = append(findings, scan.Findings...)
	}
	return findings
}

// UpdateFindingStatus updates the status of a finding
func (m *Manager) UpdateFindingStatus(findingID, status string) bool {
	m.mu.Lock()
	defer m.mu.Unlock()

	for _, scan := range m.scans {
		for i := range scan.Findings {
			if scan.Findings[i].ID == findingID {
				scan.Findings[i].Status = status
				return true
			}
		}
	}
	return false
}

// GenerateSBOM generates a Software Bill of Materials
func (m *Manager) GenerateSBOM(targetPath string) *SBOM {
	sbom := &SBOM{
		ID:          uuid.New().String(),
		Format:      "cyclonedx",
		Version:     "1.5",
		GeneratedAt: time.Now(),
		Components:  m.generateSampleComponents(),
	}
	return sbom
}

func (m *Manager) generateSampleComponents() []Component {
	return []Component{
		{
			Name:     "react",
			Version:  "18.2.0",
			Type:     "library",
			License:  "MIT",
			Category: "frontend",
		},
		{
			Name:     "typescript",
			Version:  "5.3.3",
			Type:     "library",
			License:  "Apache-2.0",
			Category: "tooling",
		},
		{
			Name:     "express",
			Version:  "4.18.2",
			Type:     "framework",
			License:  "MIT",
			Category: "backend",
		},
		{
			Name:     "lodash",
			Version:  "4.17.21",
			Type:     "library",
			License:  "MIT",
			Category: "utility",
		},
	}
}

// GetRecommendations generates recommendations based on findings
func (m *Manager) GetRecommendations() []Recommendation {
	findings := m.GetAllFindings()
	recs := []Recommendation{}

	// Group by category
	hasVulns := false
	hasSecrets := false
	hasSAST := false

	for _, f := range findings {
		switch m.tools[f.Tool].Category {
		case "dependencies", "container":
			hasVulns = true
		case "secrets":
			hasSecrets = true
		case "sast":
			hasSAST = true
		}
	}

	if hasVulns {
		recs = append(recs, Recommendation{
			Priority:    "HIGH",
			Category:    "DEPENDENCIES",
			Title:       "Update vulnerable dependencies",
			Description: "Several dependencies have known vulnerabilities. Run 'npm audit fix' or equivalent.",
		})
	}

	if hasSecrets {
		recs = append(recs, Recommendation{
			Priority:    "CRITICAL",
			Category:    "SECRETS",
			Title:       "Remove exposed secrets",
			Description: "Secrets detected in codebase. Remove and rotate immediately.",
		})
	}

	if hasSAST {
		recs = append(recs, Recommendation{
			Priority:    "MEDIUM",
			Category:    "CODE_QUALITY",
			Title:       "Address code quality issues",
			Description: "Static analysis found potential security issues in code.",
		})
	}

	return recs
}

// Recommendation represents a security recommendation
type Recommendation struct {
	Priority    string `json:"priority"`
	Category    string `json:"category"`
	Title       string `json:"title"`
	Description string `json:"description"`
}
