package resolver

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"zsvo/pkg/cache"
	"zsvo/pkg/deps"
)

// FastResolver provides high-performance dependency resolution using cached indices
type FastResolver struct {
	indexCache   *cache.IndexCache
	packageIndex *PackageIndex
	mu           sync.RWMutex
}

// PackageIndex wraps the cache PackageIndex with additional resolver functionality
type PackageIndex struct {
	*cache.PackageIndex
}

// NewFastResolver creates a new fast dependency resolver
func NewFastResolver(cacheDir string) *FastResolver {
	return &FastResolver{
		indexCache: cache.NewIndexCache(cacheDir),
	}
}

// LoadIndex loads the package index for the given repository configuration
func (r *FastResolver) LoadIndex(mirror, suite, component string) error {
	r.mu.Lock()
	defer r.mu.Unlock()

	idx, err := r.indexCache.GetIndex(mirror, suite, component)
	if err != nil {
		return err
	}

	r.packageIndex = &PackageIndex{PackageIndex: idx}
	return nil
}

// ResolvePackage resolves a package name to its source information in O(1) time
func (r *FastResolver) ResolvePackage(name string) (*SourcePackage, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()

	if r.packageIndex == nil {
		return nil, fmt.Errorf("no package index loaded")
	}

	// Try direct package name lookup first
	entry, found := r.packageIndex.LookupPackage(name)
	if found {
		return r.entryToSourcePackage(entry), nil
	}

	// Try binary package lookup
	source, found := r.packageIndex.LookupBinary(name)
	if found {
		entry, found = r.packageIndex.LookupPackage(source)
		if found {
			return r.entryToSourcePackage(entry), nil
		}
	}

	return nil, fmt.Errorf("package %s not found", name)
}

// ResolveDependencies recursively resolves all dependencies for a package
func (r *FastResolver) ResolveDependencies(rootPackage string) (*DependencyGraph, error) {
	r.mu.RLock()
	defer r.mu.RUnlock()

	if r.packageIndex == nil {
		return nil, fmt.Errorf("no package index loaded")
	}

	graph := NewDependencyGraph()
	visited := make(map[string]bool)

	// Start recursive resolution
	if err := r.resolveDependenciesRecursive(rootPackage, graph, visited, nil); err != nil {
		return nil, err
	}

	// Calculate build order
	if err := graph.CalculateBuildOrder(); err != nil {
		return nil, err
	}

	return graph, nil
}

// resolveDependenciesRecursive builds the dependency graph recursively
func (r *FastResolver) resolveDependenciesRecursive(pkgName string, graph *DependencyGraph, visited map[string]bool, path []string) error {
	// Check for cycles
	for _, p := range path {
		if p == pkgName {
			return fmt.Errorf("dependency cycle detected: %s -> %s", strings.Join(path, " -> "), pkgName)
		}
	}

	// Skip if already visited
	if visited[pkgName] {
		return nil
	}
	visited[pkgName] = true

	// Resolve the package
	pkg, err := r.ResolvePackage(pkgName)
	if err != nil {
		return err
	}

	// Add to graph
	graph.AddPackage(pkg)

	// Recursively resolve dependencies
	for _, dep := range pkg.BuildDepends {
		depName := extractPackageName(dep)
		if depName == "" {
			continue
		}

		// Skip if it's a system package that doesn't need building
		if isSystemPackage(depName) {
			continue
		}

		// Add dependency relationship
		graph.AddDependency(pkgName, depName)

		// Recursively resolve
		newPath := append(path, pkgName)
		if err := r.resolveDependenciesRecursive(depName, graph, visited, newPath); err != nil {
			return err
		}
	}

	return nil
}

// GetBuildOrder returns the packages in build order (dependencies first)
func (r *FastResolver) GetBuildOrder(rootPackage string) ([]*SourcePackage, error) {
	graph, err := r.ResolveDependencies(rootPackage)
	if err != nil {
		return nil, err
	}

	return graph.GetBuildOrder(), nil
}

// entryToSourcePackage converts a cache entry to a SourcePackage
func (r *FastResolver) entryToSourcePackage(entry *cache.PackageEntry) *SourcePackage {
	return &SourcePackage{
		Name:         entry.Package,
		Version:      entry.Version,
		Directory:    entry.Directory,
		DSCName:      entry.DSCName,
		DSCSHA256:    entry.DSCSHA256,
		Binaries:     entry.Binaries,
		BuildDepends: entry.BuildDepends,
	}
}

// AddPackage adds a package to the current index (for updating cache)
func (r *FastResolver) AddPackage(entry *cache.PackageEntry) error {
	r.mu.Lock()
	defer r.mu.Unlock()

	if r.packageIndex == nil {
		return fmt.Errorf("no package index loaded")
	}

	r.packageIndex.AddPackage(entry)
	return nil
}

// GetStats returns resolver statistics
func (r *FastResolver) GetStats() (int, int, bool) {
	r.mu.RLock()
	defer r.mu.RUnlock()

	if r.packageIndex == nil {
		return 0, 0, false
	}

	pkgCount, binCount := r.packageIndex.GetStats()
	expired := r.packageIndex.IsExpired(24 * time.Hour) // Consider expired after 24 hours

	return pkgCount, binCount, expired
}

// SaveIndex persists the current index to disk
func (r *FastResolver) SaveIndex(mirror, suite, component string) error {
	r.mu.RLock()
	defer r.mu.RUnlock()

	if r.packageIndex == nil {
		return fmt.Errorf("no package index loaded")
	}

	cacheFile := r.indexCache.CacheFilePath(mirror, suite, component)
	return r.packageIndex.Save(cacheFile)
}

// SourcePackage represents a Debian source package
type SourcePackage struct {
	Name         string
	Version      string
	Directory    string
	DSCName      string
	DSCSHA256    string
	Binaries     []string
	BuildDepends []string
}

// extractPackageName extracts package name from dependency string
func extractPackageName(dep string) string {
	// Parse dependency constraints using existing deps package
	req, err := deps.ParseRequirement(dep)
	if err != nil {
		return ""
	}

	if len(req.Alternatives) > 0 {
		return req.Alternatives[0].Name // Take first alternative
	}

	return ""
}

// isSystemPackage checks if a package is a system package that doesn't need building
func isSystemPackage(name string) bool {
	systemPackages := map[string]bool{
		"gcc":                 true,
		"g++":                 true,
		"make":                true,
		"bash":                true,
		"glibc":               true,
		"libc6":               true,
		"libc-bin":            true,
		"base-files":          true,
		"base-passwd":         true,
		"coreutils":           true,
		"dash":                true,
		"debianutils":         true,
		"diffutils":           true,
		"dpkg":                true,
		"findutils":           true,
		"grep":                true,
		"gzip":                true,
		"hostname":            true,
		"init-system-helpers": true,
		"login":               true,
		"ncurses-base":        true,
		"ncurses-bin":         true,
		"perl-base":           true,
		"sed":                 true,
		"sysvinit":            true,
		"util-linux":          true,
	}

	return systemPackages[name]
}
