package content

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

	"golang.org/x/net/html"

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

// Enhancer combines parsing and content injection
type Enhancer struct {
	parser   *parser.Parser
	injector *Injector
}

// NewEnhancer creates a new HTML enhancer
func NewEnhancer(client ContentClient, siteID string) *Enhancer {
	return &Enhancer{
		parser:   parser.New(),
		injector: NewInjector(client, siteID),
	}
}

// EnhanceFile processes an HTML file and injects content
func (e *Enhancer) EnhanceFile(inputPath, outputPath string) error {
	// Use parser to get elements from file
	result, err := e.parser.ParseDirectory(filepath.Dir(inputPath))
	if err != nil {
		return fmt.Errorf("parsing file: %w", err)
	}

	// Filter elements for this specific file
	var fileElements []parser.Element
	inputBaseName := filepath.Base(inputPath)
	for _, elem := range result.Elements {
		elemBaseName := filepath.Base(elem.FilePath)
		if elemBaseName == inputBaseName {
			fileElements = append(fileElements, elem)
		}
	}

	if len(fileElements) == 0 {
		// No insertr elements found, copy file as-is
		return e.copyFile(inputPath, outputPath)
	}

	// Read and parse HTML for modification
	htmlContent, err := os.ReadFile(inputPath)
	if err != nil {
		return fmt.Errorf("reading file %s: %w", inputPath, err)
	}

	doc, err := html.Parse(strings.NewReader(string(htmlContent)))
	if err != nil {
		return fmt.Errorf("parsing HTML: %w", err)
	}

	// Find and inject content for each element
	for _, elem := range fileElements {
		// Find the node in the parsed document
		// Note: This is a simplified approach - in production we'd need more robust node matching
		if err := e.injectElementContent(doc, elem); err != nil {
			fmt.Printf("⚠️  Warning: failed to inject content for %s: %v\n", elem.ContentID, err)
		}
	}

	// Inject editor assets for development
	libraryScript := GetLibraryScript(false) // Use non-minified for development debugging
	e.injector.InjectEditorAssets(doc, true, libraryScript)

	// Write enhanced HTML
	if err := e.writeHTML(doc, outputPath); err != nil {
		return fmt.Errorf("writing enhanced HTML: %w", err)
	}

	fmt.Printf("✅ Enhanced: %s → %s (%d elements)\n",
		filepath.Base(inputPath),
		filepath.Base(outputPath),
		len(fileElements))

	return nil
}

// injectElementContent finds and injects content for a specific element
func (e *Enhancer) injectElementContent(doc *html.Node, elem parser.Element) error {
	// Fetch content from database
	contentItem, err := e.injector.client.GetContent(e.injector.siteID, elem.ContentID)
	if err != nil {
		return fmt.Errorf("fetching content: %w", err)
	}

	// Find nodes with insertr class and inject content
	e.findAndInjectNodes(doc, elem, contentItem)
	return nil
}

// findAndInjectNodes finds the specific node for this element and injects content
func (e *Enhancer) findAndInjectNodes(rootNode *html.Node, elem parser.Element, contentItem *ContentItem) {
	// Use parser-based element matching to find the correct specific node
	targetNode := e.findNodeInDocument(rootNode, elem)
	if targetNode == nil {
		// Element not found - this is normal for elements without content in database
		return
	}

	// Determine content type: use database type if available, otherwise parser type
	contentType := string(elem.Type)
	if contentItem != nil {
		contentType = contentItem.Type // Database is source of truth
	}

	// Inject content attributes for the correctly matched node
	e.injector.AddContentAttributes(targetNode, elem.ContentID, contentType)

	// Inject content if available
	if contentItem != nil {
		switch contentItem.Type { // Use database type, not parser type
		case "text":
			e.injector.injectTextContent(targetNode, contentItem.Value)
		case "markdown":
			e.injector.injectMarkdownContent(targetNode, contentItem.Value)
		case "link":
			e.injector.injectLinkContent(targetNode, contentItem.Value)
		}
	}
}

// Helper functions are now provided by the parser package

// EnhanceDirectory processes all HTML files in a directory
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())
		}

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

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

// 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)
}

// writeHTML writes an HTML document to a file
func (e *Enhancer) writeHTML(doc *html.Node, outputPath string) error {
	// Create directory for output
	if err := os.MkdirAll(filepath.Dir(outputPath), 0755); err != nil {
		return err
	}

	// Create output file
	file, err := os.Create(outputPath)
	if err != nil {
		return err
	}
	defer file.Close()

	// Write HTML
	return html.Render(file, doc)
}

// EnhanceInPlace performs in-place enhancement of static site files
func (e *Enhancer) EnhanceInPlace(sitePath string, siteID string) error {
	// Update the injector with the correct siteID
	e.injector.siteID = siteID

	// Use existing parser logic to discover elements
	result, err := e.parser.ParseDirectory(sitePath)
	if err != nil {
		return fmt.Errorf("parsing directory: %w", err)
	}

	if len(result.Elements) == 0 {
		fmt.Printf("📄 No insertr elements found in %s\n", sitePath)
		return nil
	}

	// Group elements by file for efficient processing
	fileElements := make(map[string][]parser.Element)
	for _, elem := range result.Elements {
		fileElements[elem.FilePath] = append(fileElements[elem.FilePath], elem)
	}

	// Process each file in-place
	enhancedCount := 0
	for filePath, elements := range fileElements {
		if err := e.enhanceFileInPlace(filePath, elements); err != nil {
			fmt.Printf("⚠️ Failed to enhance %s: %v\n", filepath.Base(filePath), err)
		} else {
			enhancedCount++
		}
	}

	fmt.Printf("✅ Enhanced %d files with %d elements in site %s\n",
		enhancedCount, len(result.Elements), siteID)

	return nil
}

// enhanceFileInPlace modifies an HTML file in-place with database content
func (e *Enhancer) enhanceFileInPlace(filePath string, elements []parser.Element) error {
	// Read original file
	htmlContent, err := os.ReadFile(filePath)
	if err != nil {
		return fmt.Errorf("reading file: %w", err)
	}

	// Parse HTML
	doc, err := html.Parse(strings.NewReader(string(htmlContent)))
	if err != nil {
		return fmt.Errorf("parsing HTML: %w", err)
	}

	// Convert parser elements to injector format with content IDs
	elementIDs := make([]ElementWithID, 0, len(elements))
	for _, elem := range elements {
		// Find the corresponding node in the parsed document
		node := e.findNodeInDocument(doc, elem)
		if node != nil {
			elementIDs = append(elementIDs, ElementWithID{
				Element: &Element{
					Node: node,
					Type: string(elem.Type),
					Tag:  elem.Tag,
				},
				ContentID: elem.ContentID,
			})
		}
	}

	// Use existing bulk injection logic for efficiency
	if len(elementIDs) > 0 {
		if err := e.injector.InjectBulkContent(elementIDs); err != nil {
			return fmt.Errorf("injecting content: %w", err)
		}
	}

	// Write enhanced HTML back to the same file (in-place update)
	return e.writeHTML(doc, filePath)
}

// findNodeInDocument finds a specific node in the HTML document tree using parser utilities
func (e *Enhancer) findNodeInDocument(doc *html.Node, elem parser.Element) *html.Node {
	// Use parser's sophisticated matching logic
	return parser.FindElementInDocument(doc, elem)
}

// All element matching functions are now provided by the parser package
