package api

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/go-chi/chi/v5"
	"github.com/insertr/insertr/internal/auth"
	"github.com/insertr/insertr/internal/content"
	"github.com/insertr/insertr/internal/db"
	"github.com/insertr/insertr/internal/db/postgresql"
	"github.com/insertr/insertr/internal/db/sqlite"
	"github.com/insertr/insertr/internal/engine"
)

// ContentHandler handles all content-related HTTP requests
type ContentHandler struct {
	database    *db.Database
	authService *auth.AuthService
	siteManager *content.SiteManager
	engine      *engine.ContentEngine
}

// NewContentHandler creates a new content handler
func NewContentHandler(database *db.Database, authService *auth.AuthService) *ContentHandler {
	// Create database client for engine
	dbClient := engine.NewDatabaseClient(database)

	return &ContentHandler{
		database:    database,
		authService: authService,
		siteManager: nil, // Will be set via SetSiteManager
		engine:      engine.NewContentEngine(dbClient),
	}
}

// SetSiteManager sets the site manager for file enhancement
func (h *ContentHandler) SetSiteManager(siteManager *content.SiteManager) {
	h.siteManager = siteManager
}

// EnhanceSite handles POST /api/enhance - manual site enhancement trigger
func (h *ContentHandler) EnhanceSite(w http.ResponseWriter, r *http.Request) {
	siteID := r.URL.Query().Get("site_id")
	if siteID == "" {
		http.Error(w, "site_id parameter is required", http.StatusBadRequest)
		return
	}

	if h.siteManager == nil {
		http.Error(w, "Site manager not available", http.StatusServiceUnavailable)
		return
	}

	// Check if site is registered
	site, exists := h.siteManager.GetSite(siteID)
	if !exists {
		http.Error(w, fmt.Sprintf("Site %s is not registered", siteID), http.StatusNotFound)
		return
	}

	// Perform enhancement
	err := h.siteManager.EnhanceSite(siteID)
	if err != nil {
		log.Printf("❌ Manual enhancement failed for site %s: %v", siteID, err)
		http.Error(w, fmt.Sprintf("Enhancement failed: %v", err), http.StatusInternalServerError)
		return
	}

	// Get enhancement statistics
	stats := h.siteManager.GetStats()

	// Return success response with details
	response := map[string]interface{}{
		"success":   true,
		"site_id":   siteID,
		"site_path": site.Path,
		"message":   fmt.Sprintf("Successfully enhanced site %s", siteID),
		"stats":     stats,
		"timestamp": time.Now().Format(time.RFC3339),
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)

	log.Printf("✅ Manual enhancement completed for site %s", siteID)
}

// GetContent handles GET /api/content/{id}
func (h *ContentHandler) GetContent(w http.ResponseWriter, r *http.Request) {
	contentID := chi.URLParam(r, "id")
	siteID := r.URL.Query().Get("site_id")

	if siteID == "" {
		http.Error(w, "site_id parameter is required", http.StatusBadRequest)
		return
	}

	var content interface{}
	var err error

	switch h.database.GetDBType() {
	case "sqlite3":
		content, err = h.database.GetSQLiteQueries().GetContent(context.Background(), sqlite.GetContentParams{
			ID:     contentID,
			SiteID: siteID,
		})
	case "postgresql":
		content, err = h.database.GetPostgreSQLQueries().GetContent(context.Background(), postgresql.GetContentParams{
			ID:     contentID,
			SiteID: siteID,
		})
	default:
		http.Error(w, "Unsupported database type", http.StatusInternalServerError)
		return
	}

	if err != nil {
		if err == sql.ErrNoRows {
			http.Error(w, "Content not found", http.StatusNotFound)
			return
		}
		http.Error(w, fmt.Sprintf("Database error: %v", err), http.StatusInternalServerError)
		return
	}

	item := h.convertToAPIContent(content)

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(item)
}

// GetAllContent handles GET /api/content
func (h *ContentHandler) GetAllContent(w http.ResponseWriter, r *http.Request) {
	siteID := r.URL.Query().Get("site_id")
	if siteID == "" {
		http.Error(w, "site_id parameter is required", http.StatusBadRequest)
		return
	}

	var dbContent interface{}
	var err error

	switch h.database.GetDBType() {
	case "sqlite3":
		dbContent, err = h.database.GetSQLiteQueries().GetAllContent(context.Background(), siteID)
	case "postgresql":
		dbContent, err = h.database.GetPostgreSQLQueries().GetAllContent(context.Background(), siteID)
	default:
		http.Error(w, "Unsupported database type", http.StatusInternalServerError)
		return
	}

	if err != nil {
		http.Error(w, fmt.Sprintf("Database error: %v", err), http.StatusInternalServerError)
		return
	}

	items := h.convertToAPIContentList(dbContent)
	response := ContentResponse{Content: items}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

// GetBulkContent handles GET /api/content/bulk
func (h *ContentHandler) GetBulkContent(w http.ResponseWriter, r *http.Request) {
	siteID := r.URL.Query().Get("site_id")
	if siteID == "" {
		http.Error(w, "site_id parameter is required", http.StatusBadRequest)
		return
	}

	// Parse ids parameter
	idsParam := r.URL.Query()["ids[]"]
	if len(idsParam) == 0 {
		// Try single ids parameter
		idsStr := r.URL.Query().Get("ids")
		if idsStr == "" {
			http.Error(w, "ids parameter is required", http.StatusBadRequest)
			return
		}
		idsParam = strings.Split(idsStr, ",")
	}

	var dbContent interface{}
	var err error

	switch h.database.GetDBType() {
	case "sqlite3":
		dbContent, err = h.database.GetSQLiteQueries().GetBulkContent(context.Background(), sqlite.GetBulkContentParams{
			SiteID: siteID,
			Ids:    idsParam,
		})
	case "postgresql":
		dbContent, err = h.database.GetPostgreSQLQueries().GetBulkContent(context.Background(), postgresql.GetBulkContentParams{
			SiteID: siteID,
			Ids:    idsParam,
		})
	default:
		http.Error(w, "Unsupported database type", http.StatusInternalServerError)
		return
	}

	if err != nil {
		http.Error(w, fmt.Sprintf("Database error: %v", err), http.StatusInternalServerError)
		return
	}

	items := h.convertToAPIContentList(dbContent)
	response := ContentResponse{Content: items}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

// CreateContent handles POST /api/content
func (h *ContentHandler) CreateContent(w http.ResponseWriter, r *http.Request) {
	var req CreateContentRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid JSON", http.StatusBadRequest)
		return
	}

	siteID := r.URL.Query().Get("site_id")
	if siteID == "" {
		siteID = req.SiteID // fallback to request body
	}
	if siteID == "" {
		siteID = "default" // final fallback
	}

	// Generate content ID using the unified engine
	if req.HTMLMarkup == "" {
		http.Error(w, "html_markup is required", http.StatusBadRequest)
		return
	}

	result, engineErr := h.engine.ProcessContent(engine.ContentInput{
		HTML:     []byte(req.HTMLMarkup),
		FilePath: req.FilePath,
		SiteID:   siteID,
		Mode:     engine.IDGeneration,
	})
	if engineErr != nil {
		http.Error(w, fmt.Sprintf("ID generation failed: %v", engineErr), http.StatusInternalServerError)
		return
	}

	if len(result.Elements) == 0 {
		http.Error(w, "No insertr elements found in HTML markup", http.StatusBadRequest)
		return
	}

	// Use the ID generated by the engine for the first element
	contentID := result.Elements[0].ID

	// Extract user from request using authentication service
	userInfo, authErr := h.authService.ExtractUserFromRequest(r)
	if authErr != nil {
		http.Error(w, fmt.Sprintf("Authentication error: %v", authErr), http.StatusUnauthorized)
		return
	}
	userID := userInfo.ID

	// Check if content exists for version history (non-blocking)
	var existingContent interface{}
	var contentExists bool

	switch h.database.GetDBType() {
	case "sqlite3":
		existingContent, _ = h.database.GetSQLiteQueries().GetContent(context.Background(), sqlite.GetContentParams{
			ID:     contentID,
			SiteID: siteID,
		})
		contentExists = existingContent != nil
	case "postgresql":
		existingContent, _ = h.database.GetPostgreSQLQueries().GetContent(context.Background(), postgresql.GetContentParams{
			ID:     contentID,
			SiteID: siteID,
		})
		contentExists = existingContent != nil
	}

	// Archive existing version before upsert (only if content already exists)
	if contentExists {
		if err := h.createContentVersion(existingContent); err != nil {
			// Log error but don't fail the request - version history is non-critical
			fmt.Printf("Warning: Failed to create content version: %v\n", err)
		}
	}

	// Determine content type: use provided type, fallback to existing type, default to "text"
	contentType := req.Type
	if contentType == "" && contentExists {
		contentType = h.getContentType(existingContent)
	}
	if contentType == "" {
		contentType = "text" // default type for new content
	}

	var content interface{}
	var err error

	switch h.database.GetDBType() {
	case "sqlite3":
		content, err = h.database.GetSQLiteQueries().UpsertContent(context.Background(), sqlite.UpsertContentParams{
			ID:           contentID,
			SiteID:       siteID,
			Value:        req.Value,
			Type:         contentType,
			LastEditedBy: userID,
		})
	case "postgresql":
		content, err = h.database.GetPostgreSQLQueries().UpsertContent(context.Background(), postgresql.UpsertContentParams{
			ID:           contentID,
			SiteID:       siteID,
			Value:        req.Value,
			Type:         contentType,
			LastEditedBy: userID,
		})
	default:
		http.Error(w, "Unsupported database type", http.StatusInternalServerError)
		return
	}

	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to upsert content: %v", err), http.StatusInternalServerError)
		return
	}

	item := h.convertToAPIContent(content)

	// Trigger file enhancement if site is registered for auto-enhancement
	log.Printf("🔍 Checking auto-enhancement for site: %s", siteID)
	if h.siteManager == nil {
		log.Printf("❌ No site manager configured")
	} else if !h.siteManager.IsAutoEnhanceEnabled(siteID) {
		log.Printf("❌ Auto-enhancement not enabled for site: %s", siteID)
	} else {
		log.Printf("✅ Triggering auto-enhancement for site: %s", siteID)
		go func() {
			log.Printf("🔄 Starting enhancement for site: %s", siteID)
			if err := h.siteManager.EnhanceSite(siteID); err != nil {
				log.Printf("⚠️ Failed to enhance site %s: %v", siteID, err)
			} else {
				log.Printf("✅ Enhanced files for site %s", siteID)
			}
		}()
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(item)
}

// DeleteContent handles DELETE /api/content/{id}
func (h *ContentHandler) DeleteContent(w http.ResponseWriter, r *http.Request) {
	contentID := chi.URLParam(r, "id")
	siteID := r.URL.Query().Get("site_id")

	if siteID == "" {
		http.Error(w, "site_id parameter is required", http.StatusBadRequest)
		return
	}

	var err error

	switch h.database.GetDBType() {
	case "sqlite3":
		err = h.database.GetSQLiteQueries().DeleteContent(context.Background(), sqlite.DeleteContentParams{
			ID:     contentID,
			SiteID: siteID,
		})
	case "postgresql":
		err = h.database.GetPostgreSQLQueries().DeleteContent(context.Background(), postgresql.DeleteContentParams{
			ID:     contentID,
			SiteID: siteID,
		})
	default:
		http.Error(w, "Unsupported database type", http.StatusInternalServerError)
		return
	}

	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to delete content: %v", err), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

// GetContentVersions handles GET /api/content/{id}/versions
func (h *ContentHandler) GetContentVersions(w http.ResponseWriter, r *http.Request) {
	contentID := chi.URLParam(r, "id")
	siteID := r.URL.Query().Get("site_id")

	if siteID == "" {
		http.Error(w, "site_id parameter is required", http.StatusBadRequest)
		return
	}

	// Parse limit parameter (default to 10)
	limit := int64(10)
	if limitStr := r.URL.Query().Get("limit"); limitStr != "" {
		if parsedLimit, err := strconv.ParseInt(limitStr, 10, 64); err == nil {
			limit = parsedLimit
		}
	}

	var dbVersions interface{}
	var err error

	switch h.database.GetDBType() {
	case "sqlite3":
		dbVersions, err = h.database.GetSQLiteQueries().GetContentVersionHistory(context.Background(), sqlite.GetContentVersionHistoryParams{
			ContentID:  contentID,
			SiteID:     siteID,
			LimitCount: limit,
		})
	case "postgresql":
		// Note: PostgreSQL uses different parameter names due to int32 vs int64
		dbVersions, err = h.database.GetPostgreSQLQueries().GetContentVersionHistory(context.Background(), postgresql.GetContentVersionHistoryParams{
			ContentID:  contentID,
			SiteID:     siteID,
			LimitCount: int32(limit),
		})
	default:
		http.Error(w, "Unsupported database type", http.StatusInternalServerError)
		return
	}

	if err != nil {
		http.Error(w, fmt.Sprintf("Database error: %v", err), http.StatusInternalServerError)
		return
	}

	versions := h.convertToAPIVersionList(dbVersions)
	response := ContentVersionsResponse{Versions: versions}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

// RollbackContent handles POST /api/content/{id}/rollback
func (h *ContentHandler) RollbackContent(w http.ResponseWriter, r *http.Request) {
	contentID := chi.URLParam(r, "id")
	siteID := r.URL.Query().Get("site_id")

	if siteID == "" {
		http.Error(w, "site_id parameter is required", http.StatusBadRequest)
		return
	}

	var req RollbackContentRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Invalid JSON", http.StatusBadRequest)
		return
	}

	// Get the target version
	var targetVersion interface{}
	var err error

	switch h.database.GetDBType() {
	case "sqlite3":
		targetVersion, err = h.database.GetSQLiteQueries().GetContentVersion(context.Background(), req.VersionID)
	case "postgresql":
		targetVersion, err = h.database.GetPostgreSQLQueries().GetContentVersion(context.Background(), int32(req.VersionID))
	default:
		http.Error(w, "Unsupported database type", http.StatusInternalServerError)
		return
	}

	if err != nil {
		if err == sql.ErrNoRows {
			http.Error(w, "Version not found", http.StatusNotFound)
			return
		}
		http.Error(w, fmt.Sprintf("Database error: %v", err), http.StatusInternalServerError)
		return
	}

	// Verify the version belongs to the correct content
	if !h.versionMatches(targetVersion, contentID, siteID) {
		http.Error(w, "Version does not match content", http.StatusBadRequest)
		return
	}

	// Extract user from request using authentication service
	userInfo, authErr := h.authService.ExtractUserFromRequest(r)
	if authErr != nil {
		http.Error(w, fmt.Sprintf("Authentication error: %v", authErr), http.StatusUnauthorized)
		return
	}
	userID := userInfo.ID

	// Archive current version before rollback
	var currentContent interface{}

	switch h.database.GetDBType() {
	case "sqlite3":
		currentContent, err = h.database.GetSQLiteQueries().GetContent(context.Background(), sqlite.GetContentParams{
			ID:     contentID,
			SiteID: siteID,
		})
	case "postgresql":
		currentContent, err = h.database.GetPostgreSQLQueries().GetContent(context.Background(), postgresql.GetContentParams{
			ID:     contentID,
			SiteID: siteID,
		})
	default:
		http.Error(w, "Unsupported database type", http.StatusInternalServerError)
		return
	}

	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to get current content: %v", err), http.StatusInternalServerError)
		return
	}

	err = h.createContentVersion(currentContent)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to create version: %v", err), http.StatusInternalServerError)
		return
	}

	// Rollback to target version
	var updatedContent interface{}

	switch h.database.GetDBType() {
	case "sqlite3":
		sqliteVersion := targetVersion.(sqlite.ContentVersion)
		updatedContent, err = h.database.GetSQLiteQueries().UpdateContent(context.Background(), sqlite.UpdateContentParams{
			Value:        sqliteVersion.Value,
			Type:         sqliteVersion.Type,
			LastEditedBy: userID,
			ID:           contentID,
			SiteID:       siteID,
		})
	case "postgresql":
		pgVersion := targetVersion.(postgresql.ContentVersion)
		updatedContent, err = h.database.GetPostgreSQLQueries().UpdateContent(context.Background(), postgresql.UpdateContentParams{
			Value:        pgVersion.Value,
			Type:         pgVersion.Type,
			LastEditedBy: userID,
			ID:           contentID,
			SiteID:       siteID,
		})
	default:
		http.Error(w, "Unsupported database type", http.StatusInternalServerError)
		return
	}

	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to rollback content: %v", err), http.StatusInternalServerError)
		return
	}

	item := h.convertToAPIContent(updatedContent)

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(item)
}

// Helper functions for type conversion
func (h *ContentHandler) convertToAPIContent(content interface{}) ContentItem {
	switch h.database.GetDBType() {
	case "sqlite3":
		c := content.(sqlite.Content)
		return ContentItem{
			ID:           c.ID,
			SiteID:       c.SiteID,
			Value:        c.Value,
			Type:         c.Type,
			CreatedAt:    time.Unix(c.CreatedAt, 0),
			UpdatedAt:    time.Unix(c.UpdatedAt, 0),
			LastEditedBy: c.LastEditedBy,
		}
	case "postgresql":
		c := content.(postgresql.Content)
		return ContentItem{
			ID:           c.ID,
			SiteID:       c.SiteID,
			Value:        c.Value,
			Type:         c.Type,
			CreatedAt:    time.Unix(c.CreatedAt, 0),
			UpdatedAt:    time.Unix(c.UpdatedAt, 0),
			LastEditedBy: c.LastEditedBy,
		}
	}
	return ContentItem{} // Should never happen
}

func (h *ContentHandler) convertToAPIContentList(contentList interface{}) []ContentItem {
	switch h.database.GetDBType() {
	case "sqlite3":
		list := contentList.([]sqlite.Content)
		items := make([]ContentItem, len(list))
		for i, content := range list {
			items[i] = h.convertToAPIContent(content)
		}
		return items
	case "postgresql":
		list := contentList.([]postgresql.Content)
		items := make([]ContentItem, len(list))
		for i, content := range list {
			items[i] = h.convertToAPIContent(content)
		}
		return items
	}
	return []ContentItem{} // Should never happen
}

func (h *ContentHandler) convertToAPIVersionList(versionList interface{}) []ContentVersion {
	switch h.database.GetDBType() {
	case "sqlite3":
		list := versionList.([]sqlite.ContentVersion)
		versions := make([]ContentVersion, len(list))
		for i, version := range list {
			versions[i] = ContentVersion{
				VersionID: version.VersionID,
				ContentID: version.ContentID,
				SiteID:    version.SiteID,
				Value:     version.Value,
				Type:      version.Type,
				CreatedAt: time.Unix(version.CreatedAt, 0),
				CreatedBy: version.CreatedBy,
			}
		}
		return versions
	case "postgresql":
		list := versionList.([]postgresql.ContentVersion)
		versions := make([]ContentVersion, len(list))
		for i, version := range list {
			versions[i] = ContentVersion{
				VersionID: int64(version.VersionID),
				ContentID: version.ContentID,
				SiteID:    version.SiteID,
				Value:     version.Value,
				Type:      version.Type,
				CreatedAt: time.Unix(version.CreatedAt, 0),
				CreatedBy: version.CreatedBy,
			}
		}
		return versions
	}
	return []ContentVersion{} // Should never happen
}

func (h *ContentHandler) createContentVersion(content interface{}) error {
	switch h.database.GetDBType() {
	case "sqlite3":
		c := content.(sqlite.Content)
		return h.database.GetSQLiteQueries().CreateContentVersion(context.Background(), sqlite.CreateContentVersionParams{
			ContentID: c.ID,
			SiteID:    c.SiteID,
			Value:     c.Value,
			Type:      c.Type,
			CreatedBy: c.LastEditedBy,
		})
	case "postgresql":
		c := content.(postgresql.Content)
		return h.database.GetPostgreSQLQueries().CreateContentVersion(context.Background(), postgresql.CreateContentVersionParams{
			ContentID: c.ID,
			SiteID:    c.SiteID,
			Value:     c.Value,
			Type:      c.Type,
			CreatedBy: c.LastEditedBy,
		})
	}
	return fmt.Errorf("unsupported database type")
}

func (h *ContentHandler) getContentType(content interface{}) string {
	switch h.database.GetDBType() {
	case "sqlite3":
		return content.(sqlite.Content).Type
	case "postgresql":
		return content.(postgresql.Content).Type
	}
	return ""
}

func (h *ContentHandler) versionMatches(version interface{}, contentID, siteID string) bool {
	switch h.database.GetDBType() {
	case "sqlite3":
		v := version.(sqlite.ContentVersion)
		return v.ContentID == contentID && v.SiteID == siteID
	case "postgresql":
		v := version.(postgresql.ContentVersion)
		return v.ContentID == contentID && v.SiteID == siteID
	}
	return false
}

// generateContentID function removed - using unified ContentEngine instead

// ServeInsertrJS handles GET /insertr.js - serves the insertr JavaScript library
func (h *ContentHandler) ServeInsertrJS(w http.ResponseWriter, r *http.Request) {
	// Path to the built insertr.js file
	jsPath := "lib/dist/insertr.js"

	// Check if file exists
	if _, err := os.Stat(jsPath); os.IsNotExist(err) {
		http.Error(w, "insertr.js not found - run 'just build-lib' to build the library", http.StatusNotFound)
		return
	}

	// Open and serve the file
	file, err := os.Open(jsPath)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to open insertr.js: %v", err), http.StatusInternalServerError)
		return
	}
	defer file.Close()

	// Set appropriate headers
	w.Header().Set("Content-Type", "application/javascript")
	w.Header().Set("Cache-Control", "no-cache") // For development

	// Copy file contents to response
	io.Copy(w, file)
}

// ServeInsertrCSS handles GET /insertr.css - serves the insertr CSS stylesheet
func (h *ContentHandler) ServeInsertrCSS(w http.ResponseWriter, r *http.Request) {
	// Path to the built insertr.css file
	cssPath := "lib/dist/insertr.css"

	// Check if file exists
	if _, err := os.Stat(cssPath); os.IsNotExist(err) {
		http.Error(w, "insertr.css not found - run 'just build-lib' to build the library", http.StatusNotFound)
		return
	}

	// Open and serve the file
	file, err := os.Open(cssPath)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to open insertr.css: %v", err), http.StatusInternalServerError)
		return
	}
	defer file.Close()

	// Set appropriate headers
	w.Header().Set("Content-Type", "text/css")
	w.Header().Set("Cache-Control", "no-cache") // For development

	// Copy file contents to response
	io.Copy(w, file)
}
