package content

import (
	"fmt"
	"golang.org/x/net/html"
	"os"
	"path/filepath"
	"strings"

	"github.com/insertr/insertr/internal/engine"
)

// EnhancementConfig configures the enhancement pipeline
type EnhancementConfig struct {
	Discovery        DiscoveryConfig
	ContentInjection bool
	GenerateIDs      bool
}

// DiscoveryConfig configures element discovery
type DiscoveryConfig struct {
	Enabled    bool
	Aggressive bool
	Containers bool
	Individual bool
}

// Enhancer combines discovery, ID generation, and content injection in unified pipeline
type Enhancer struct {
	engine     *engine.ContentEngine
	discoverer *Discoverer
	config     EnhancementConfig
	siteID     string
}

// NewEnhancer creates a new HTML enhancer with unified pipeline
func NewEnhancer(client engine.ContentClient, siteID string, config EnhancementConfig) *Enhancer {
	return &Enhancer{
		engine:     engine.NewContentEngine(client),
		discoverer: NewDiscoverer(),
		config:     config,
		siteID:     siteID,
	}
}

// NewDefaultEnhancer creates an enhancer with default configuration
func NewDefaultEnhancer(client engine.ContentClient, siteID string) *Enhancer {
	defaultConfig := EnhancementConfig{
		Discovery: DiscoveryConfig{
			Enabled:    true,
			Aggressive: false,
			Containers: true,
			Individual: true,
		},
		ContentInjection: true,
		GenerateIDs:      true,
	}
	return NewEnhancer(client, siteID, defaultConfig)
}

// EnhanceFile processes a single HTML file through the complete pipeline
func (e *Enhancer) EnhanceFile(inputPath, outputPath string) error {
	// Read HTML file
	htmlContent, err := os.ReadFile(inputPath)
	if err != nil {
		return fmt.Errorf("reading file %s: %w", inputPath, err)
	}

	// Process through unified pipeline
	processedHTML, err := e.processHTML(htmlContent, filepath.Base(inputPath))
	if err != nil {
		return fmt.Errorf("processing HTML %s: %w", inputPath, err)
	}

	// Create output directory
	if err := os.MkdirAll(filepath.Dir(outputPath), 0755); err != nil {
		return fmt.Errorf("creating output directory: %w", err)
	}

	// Write processed HTML
	return os.WriteFile(outputPath, processedHTML, 0644)
}

// EnhanceDirectory processes all files in a directory through the unified pipeline
func (e *Enhancer) EnhanceDirectory(inputDir, outputDir string) error {
	// Create output directory
	if err := os.MkdirAll(outputDir, 0755); err != nil {
		return fmt.Errorf("creating output directory: %w", err)
	}

	// Walk input directory
	return filepath.Walk(inputDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// Calculate relative path and output path
		relPath, err := filepath.Rel(inputDir, path)
		if err != nil {
			return err
		}
		outputPath := filepath.Join(outputDir, relPath)

		// Handle directories
		if info.IsDir() {
			return os.MkdirAll(outputPath, info.Mode())
		}

		// Process HTML files through enhancement pipeline
		if strings.HasSuffix(strings.ToLower(path), ".html") {
			return e.EnhanceFile(path, outputPath)
		}

		// Copy non-HTML files as-is
		return e.copyFile(path, outputPath)
	})
}

// processHTML implements the unified enhancement pipeline
func (e *Enhancer) processHTML(htmlContent []byte, filePath string) ([]byte, error) {
	var processedHTML []byte = htmlContent

	// Phase 1: Element Discovery (if enabled)
	if e.config.Discovery.Enabled {
		discoveredHTML, err := e.discoverElements(processedHTML, filePath)
		if err != nil {
			return nil, fmt.Errorf("element discovery: %w", err)
		}
		processedHTML = discoveredHTML
	}

	// Phase 2 & 3: ID Generation + Content Injection (via engine)
	if e.config.GenerateIDs || e.config.ContentInjection {
		enhancedHTML, err := e.enhanceWithEngine(processedHTML, filePath)
		if err != nil {
			return nil, fmt.Errorf("engine enhancement: %w", err)
		}
		processedHTML = enhancedHTML
	}

	return processedHTML, nil
}

// discoverElements adds insertr classes to viable elements
func (e *Enhancer) discoverElements(htmlContent []byte, filePath string) ([]byte, error) {
	// Parse HTML
	doc, err := html.Parse(strings.NewReader(string(htmlContent)))
	if err != nil {
		return nil, fmt.Errorf("parsing HTML: %w", err)
	}

	// Find and mark viable elements
	result := &FileDiscoveryResult{Document: doc}
	e.discoverer.discoverNode(doc, result, e.config.Discovery.Aggressive)

	// Render back to HTML
	var buf strings.Builder
	if err := html.Render(&buf, doc); err != nil {
		return nil, fmt.Errorf("rendering HTML: %w", err)
	}

	return []byte(buf.String()), nil
}

// enhanceWithEngine uses the unified engine for ID generation and content injection
func (e *Enhancer) enhanceWithEngine(htmlContent []byte, filePath string) ([]byte, error) {
	// Determine processing mode
	var mode engine.ProcessMode
	if e.config.ContentInjection {
		mode = engine.Enhancement // ID generation + content injection
	} else {
		mode = engine.IDGeneration // ID generation only
	}

	// Process with engine
	result, err := e.engine.ProcessContent(engine.ContentInput{
		HTML:     htmlContent,
		FilePath: filePath,
		SiteID:   e.siteID,
		Mode:     mode,
	})
	if err != nil {
		return nil, fmt.Errorf("engine processing: %w", err)
	}

	// Render enhanced document
	var buf strings.Builder
	if err := html.Render(&buf, result.Document); err != nil {
		return nil, fmt.Errorf("rendering enhanced HTML: %w", err)
	}

	return []byte(buf.String()), nil
}

// SetSiteID sets the site ID for the enhancer
func (e *Enhancer) SetSiteID(siteID string) {
	e.siteID = siteID
}

// EnhanceInPlace performs in-place enhancement of static site files
func (e *Enhancer) EnhanceInPlace(sitePath string, siteID string) error {
	// Use the provided siteID (derivation should happen at CLI level)
	e.siteID = siteID

	// Use EnhanceDirectory with same input and output (in-place)
	return e.EnhanceDirectory(sitePath, sitePath)
}

// DeriveOrValidateSiteID automatically derives site_id for demo paths or validates for production
func DeriveOrValidateSiteID(sitePath string, configSiteID string) string {
	// Check if this is a demo path
	if strings.Contains(sitePath, "/demos/") || strings.Contains(sitePath, "./demos/") {
		return deriveDemoSiteID(sitePath)
	}

	// For non-demo paths, return the configured site_id
	// Validation of non-demo site_id will be handled at the CLI level
	return configSiteID
}

// deriveDemoSiteID extracts site_id from demo directory structure
func deriveDemoSiteID(sitePath string) string {
	// Convert to absolute path and clean it
	absPath, err := filepath.Abs(sitePath)
	if err != nil {
		absPath = sitePath
	}
	absPath = filepath.Clean(absPath)

	// Flattened structure - just use directory name after demos/
	// demos/default -> "default"
	// demos/simple -> "simple"
	// demos/dan-eden-portfolio -> "dan-eden-portfolio"

	parts := strings.Split(absPath, string(filepath.Separator))

	// Find the demos directory index
	demosIndex := -1
	for i, part := range parts {
		if part == "demos" {
			demosIndex = i
			break
		}
	}

	if demosIndex == -1 || demosIndex >= len(parts)-1 {
		// Fallback if demos not found in path
		return "default"
	}

	// Get the segment after demos/ and clean it
	dirName := parts[demosIndex+1]
	dirName = strings.TrimSuffix(dirName, "_enhanced")

	return dirName
}

// copyFile copies a file from src to dst
func (e *Enhancer) copyFile(src, dst string) error {
	// Create directory for destination
	if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
		return err
	}

	// Read source
	data, err := os.ReadFile(src)
	if err != nil {
		return err
	}

	// Write destination
	return os.WriteFile(dst, data, 0644)
}
