package javascript

import (
	"context"
	"regexp"
	"strings"

	contractevents "bit.admedia.com/scm/ad/adpilot-indexing-code-parser.com/internal/contracts/events"
	"bit.admedia.com/scm/ad/adpilot-indexing-code-parser.com/internal/contracts/graph"
	"bit.admedia.com/scm/ad/adpilot-indexing-code-parser.com/internal/service/parser/analyzers/common"
	"bit.admedia.com/scm/ad/adpilot-indexing-code-parser.com/internal/service/parser/analyzers/detectors"
)

// Analyzer parses JavaScript and TypeScript source files.
type Analyzer struct {
	detectors *detectors.Registry
}

func NewAnalyzer(det *detectors.Registry) *Analyzer {
	if det == nil {
		det = detectors.DefaultRegistry()
	}
	return &Analyzer{detectors: det}
}

func (a *Analyzer) SupportedLanguages() []string {
	return []string{"javascript", "typescript"}
}

func (a *Analyzer) ParseFile(ctx context.Context, source []byte, filePath, language string) (*graph.Artifact, []contractevents.CodeChunk, error) {
	_ = ctx
	if language == "" {
		language = inferLanguage(filePath)
	}

	b := common.NewBuilder(filePath, language)
	lines := common.Lines(source)

	importPattern := regexp.MustCompile(`(?:import\s+.+\s+from\s+['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\))`)
	funcPattern := regexp.MustCompile(`(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][A-Za-z0-9_$]*)`)
	classPattern := regexp.MustCompile(`(?:export\s+)?class\s+([A-Za-z_$][A-Za-z0-9_$]*)`)
	arrowPattern := regexp.MustCompile(`(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*=\s*(?:async\s*)?\(`)
	methodPattern := regexp.MustCompile(`^\s*(?:async\s+)?([A-Za-z_$][A-Za-z0-9_$]*)\s*\([^)]*\)\s*\{`)

	for i, line := range lines {
		if m := importPattern.FindStringSubmatch(line); len(m) > 0 {
			imp := m[1]
			if imp == "" {
				imp = m[2]
			}
			if imp != "" {
				b.AddImport(imp, i+1)
			}
		}
		if m := funcPattern.FindStringSubmatch(line); len(m) > 1 {
			end := findBlockEnd(lines, i)
			b.AddSymbol(m[1], contractevents.SymbolTypeFunction, i+1, end, nil)
		}
		if m := classPattern.FindStringSubmatch(line); len(m) > 1 {
			end := findBlockEnd(lines, i)
			b.AddSymbol(m[1], contractevents.SymbolTypeType, i+1, end, map[string]string{"type_kind": "class"})
		}
		if m := arrowPattern.FindStringSubmatch(line); len(m) > 1 {
			b.AddSymbol(m[1], contractevents.SymbolTypeFunction, i+1, i+1, map[string]string{"symbol_kind": "arrow"})
		}
		if m := methodPattern.FindStringSubmatch(line); len(m) > 1 {
			if m[1] != "if" && m[1] != "for" && m[1] != "while" && m[1] != "switch" {
				end := findBlockEnd(lines, i)
				b.AddSymbol(m[1], contractevents.SymbolTypeMethod, i+1, end, nil)
			}
		}
	}

	a.detectors.ApplyAll(detectors.Context{
		Source:   source,
		FilePath: filePath,
		Language: language,
		Builder:  b,
	})

	b.EnsureLineWindowChunks(source)

	return b.Artifact(), b.Chunks(), nil
}

func inferLanguage(filePath string) string {
	lower := strings.ToLower(filePath)
	switch {
	case strings.HasSuffix(lower, ".ts"), strings.HasSuffix(lower, ".tsx"):
		return "typescript"
	default:
		return "javascript"
	}
}

func findBlockEnd(lines []string, start int) int {
	depth := 0
	started := false
	for i := start; i < len(lines); i++ {
		for _, ch := range lines[i] {
			switch ch {
			case '{':
				depth++
				started = true
			case '}':
				depth--
				if started && depth == 0 {
					return i + 1
				}
			}
		}
	}
	if start < len(lines) {
		return start + 1
	}
	return start + 1
}
