package detectors

import (
	"context"
	"fmt"
	"path/filepath"
	"strings"

	"github.com/yourorg/devour/internal/quality"
)

// NamingConvention represents a naming convention
type NamingConvention string

const (
	ConventionKebabCase  NamingConvention = "kebab-case"
	ConventionPascalCase NamingConvention = "PascalCase"
	ConventionCamelCase  NamingConvention = "camelCase"
	ConventionSnakeCase  NamingConvention = "snake_case"
	ConventionFlatLower  NamingConvention = "flat_lower"
)

// NamingDetector detects naming inconsistencies
type NamingDetector struct {
	*quality.BaseDetector
	skipNames map[string]bool
	skipDirs  map[string]bool
}

// NamingAnalysis represents naming analysis for a directory
type NamingAnalysis struct {
	Directory       string                   `json:"directory"`
	Conventions     map[NamingConvention]int `json:"conventions"`
	TotalFiles      int                      `json:"total_files"`
	Minority        NamingConvention         `json:"minority"`
	MinorityCount   int                      `json:"minority_count"`
	MinorityPercent float64                  `json:"minority_percent"`
}

// NewNamingDetector creates a new naming detector
func NewNamingDetector(finder quality.FileFinder) *NamingDetector {
	skipNames := map[string]bool{
		"README.md":  true,
		"LICENSE":    true,
		"Makefile":   true,
		"Dockerfile": true,
		"go.mod":     true,
		"go.sum":     true,
	}

	skipDirs := map[string]bool{
		".git":         true,
		"node_modules": true,
		"vendor":       true,
		".vscode":      true,
		".idea":        true,
	}

	return &NamingDetector{
		BaseDetector: quality.NewBaseDetector("naming", quality.SeverityT2, finder),
		skipNames:    skipNames,
		skipDirs:     skipDirs,
	}
}

// Name returns the detector name
func (d *NamingDetector) Name() string {
	return "naming"
}

// Severity returns the default severity
func (d *NamingDetector) Severity() quality.Severity {
	return quality.SeverityT2
}

// Detect runs naming inconsistency detection
func (d *NamingDetector) Detect(ctx context.Context, path string, config *quality.Config) ([]quality.Finding, error) {
	files, err := d.FindFiles(path, config.Language)
	if err != nil {
		return nil, fmt.Errorf("failed to find files: %w", err)
	}

	// Group files by directory
	dirFiles := make(map[string][]string)
	for _, file := range files {
		if quality.ShouldExclude(file, config.Exclude) {
			continue
		}

		dir := filepath.Dir(file)
		dirFiles[dir] = append(dirFiles[dir], file)
	}

	var findings []quality.Finding

	// Analyze each directory
	for dir, files := range dirFiles {
		analysis := d.analyzeDirectory(dir, files)
		if d.shouldReport(analysis) {
			finding := d.createFinding(analysis)
			findings = append(findings, finding)
		}
	}

	return findings, nil
}

// analyzeDirectory analyzes naming conventions in a directory
func (d *NamingDetector) analyzeDirectory(dir string, files []string) NamingAnalysis {
	conventions := make(map[NamingConvention]int)
	totalFiles := 0

	for _, file := range files {
		filename := filepath.Base(file)

		// Skip certain files
		if d.skipNames[filename] {
			continue
		}

		// Check if we should skip this directory
		if d.skipDirs[filepath.Base(dir)] {
			continue
		}

		convention := d.classifyConvention(filename)
		if convention != "" {
			conventions[convention]++
			totalFiles++
		}
	}

	// Find minority convention
	minority, minorityCount, minorityPercent := d.findMinorityConvention(conventions, totalFiles)

	return NamingAnalysis{
		Directory:       dir,
		Conventions:     conventions,
		TotalFiles:      totalFiles,
		Minority:        minority,
		MinorityCount:   minorityCount,
		MinorityPercent: minorityPercent,
	}
}

// classifyConvention classifies a filename into a naming convention
func (d *NamingDetector) classifyConvention(filename string) NamingConvention {
	// Remove extension
	stem := filename
	if idx := strings.LastIndex(filename, "."); idx != -1 {
		stem = filename[:idx]
	}

	if stem == "" {
		return ""
	}

	// Check each convention
	if strings.Contains(stem, "-") && stem == strings.ToLower(stem) {
		return ConventionKebabCase
	}

	if len(stem) > 0 && strings.ToUpper(string(stem[0])) == string(stem[0]) && !strings.Contains(stem, "-") {
		return ConventionPascalCase
	}

	if len(stem) > 0 && strings.ToLower(string(stem[0])) == string(stem[0]) &&
		d.hasUpper(stem) && !strings.Contains(stem, "-") {
		return ConventionCamelCase
	}

	if strings.Contains(stem, "_") && stem == strings.ToLower(stem) {
		return ConventionSnakeCase
	}

	if stem == strings.ToLower(stem) && !strings.Contains(stem, "-") {
		return ConventionFlatLower
	}

	return ""
}

// hasUpper checks if a string contains uppercase letters
func (d *NamingDetector) hasUpper(s string) bool {
	for _, r := range s {
		if r >= 'A' && r <= 'Z' {
			return true
		}
	}
	return false
}

// findMinorityConvention finds the minority naming convention
func (d *NamingDetector) findMinorityConvention(conventions map[NamingConvention]int, totalFiles int) (NamingConvention, int, float64) {
	if len(conventions) < 2 {
		return "", 0, 0
	}

	var minority NamingConvention
	minorityCount := 0
	minCount := totalFiles

	for convention, count := range conventions {
		if count < minCount {
			minCount = count
			minorityCount = count
			minority = convention
		}
	}

	// Check thresholds
	minorityPercent := float64(minorityCount) / float64(totalFiles) * 100

	// Only report if minority has >= 5 files and >= 15% of total
	if minorityCount >= 5 && minorityPercent >= 15 {
		return minority, minorityCount, minorityPercent
	}

	return "", 0, 0
}

// shouldReport determines if the analysis should be reported
func (d *NamingDetector) shouldReport(analysis NamingAnalysis) bool {
	return analysis.Minority != "" &&
		analysis.MinorityCount >= 5 &&
		analysis.MinorityPercent >= 15
}

// createFinding creates a finding from analysis
func (d *NamingDetector) createFinding(analysis NamingAnalysis) quality.Finding {
	conventionList := make([]string, 0, len(analysis.Conventions))
	for conv, count := range analysis.Conventions {
		conventionList = append(conventionList, fmt.Sprintf("%s (%d)", conv, count))
	}

	return quality.Finding{
		ID:    fmt.Sprintf("naming-%s", strings.ReplaceAll(analysis.Directory, "/", "-")),
		Type:  "naming",
		Title: "Naming inconsistency detected",
		Description: fmt.Sprintf("Directory '%s' has mixed naming conventions. Minority: %s with %d files (%.1f%%). All conventions: %s",
			analysis.Directory, analysis.Minority, analysis.MinorityCount, analysis.MinorityPercent, strings.Join(conventionList, ", ")),
		File:     analysis.Directory,
		Line:     1,
		Severity: d.Severity(),
		Score:    int(analysis.MinorityPercent), // Score based on percentage
		Status:   quality.StatusOpen,
		Metadata: map[string]string{
			"directory":        analysis.Directory,
			"minority":         string(analysis.Minority),
			"minority_count":   fmt.Sprintf("%d", analysis.MinorityCount),
			"minority_percent": fmt.Sprintf("%.1f", analysis.MinorityPercent),
			"total_files":      fmt.Sprintf("%d", analysis.TotalFiles),
			"conventions":      strings.Join(conventionList, ";"),
		},
	}
}
