package rules

import (
	"fmt"
	"log"
	"strconv"

	"eq2emu/internal/database"
)

// DatabaseService handles rule database operations
// Converted from C++ WorldDatabase rule functions
type DatabaseService struct {
	db *database.DB
}

// NewDatabaseService creates a new database service instance
func NewDatabaseService(db *database.DB) *DatabaseService {
	return &DatabaseService{
		db: db,
	}
}

// LoadGlobalRuleSet loads the global rule set from database
// Converted from C++ WorldDatabase::LoadGlobalRuleSet()
func (ds *DatabaseService) LoadGlobalRuleSet(ruleManager *RuleManager) error {
	if ds.db == nil {
		return fmt.Errorf("database not initialized")
	}

	ruleSetID := int32(0)

	// Get the default ruleset ID from variables table
	query := "SELECT variable_value FROM variables WHERE variable_name = ?"
	row, err := ds.db.QueryRow(query, DefaultRuleSetIDVar)
	if err != nil {
		return fmt.Errorf("error querying default ruleset ID: %v", err)
	}

	if row == nil {
		log.Printf("[Rules] Variables table is missing %s variable name, using code-default rules", DefaultRuleSetIDVar)
		return nil
	}
	defer row.Close()

	variableValue := row.Text(0)
	if id, err := strconv.ParseInt(variableValue, 10, 32); err == nil {
		ruleSetID = int32(id)
		log.Printf("[Rules] Loading Global Ruleset id %d", ruleSetID)
	} else {
		return fmt.Errorf("invalid ruleset ID format: %s", variableValue)
	}

	if ruleSetID > 0 {
		if !ruleManager.SetGlobalRuleSet(ruleSetID) {
			return fmt.Errorf("error loading global rule set - rule set with ID %d does not exist", ruleSetID)
		}
	}

	ruleManager.stats.IncrementDatabaseOperations()
	return nil
}

// LoadRuleSets loads all rule sets from database
// Converted from C++ WorldDatabase::LoadRuleSets()
func (ds *DatabaseService) LoadRuleSets(ruleManager *RuleManager, reload bool) error {
	if ds.db == nil {
		return fmt.Errorf("database not initialized")
	}

	if reload {
		ruleManager.Flush(true)
	}

	// First load the coded defaults into the global rule set
	ruleManager.LoadCodedDefaultsIntoRuleSet(ruleManager.GetGlobalRuleSet())

	// Load active rule sets from database
	query := "SELECT ruleset_id, ruleset_name FROM rulesets WHERE ruleset_active > 0"
	loadedCount := 0

	err := ds.db.Query(query, func(row *database.Row) error {
		ruleSetID := int32(row.Int64(0))
		ruleSetName := row.Text(1)

		ruleSet := NewRuleSet()
		ruleSet.SetID(ruleSetID)
		ruleSet.SetName(ruleSetName)

		if ruleManager.AddRuleSet(ruleSet) {
			log.Printf("[Rules] Loading rule set '%s' (%d)", ruleSet.GetName(), ruleSet.GetID())

			err := ds.LoadRuleSetDetails(ruleManager, ruleSet)
			if err != nil {
				log.Printf("[Rules] Error loading rule set details for '%s': %v", ruleSetName, err)
				return nil // Continue with other rule sets
			}
			loadedCount++
		} else {
			log.Printf("[Rules] Unable to add rule set '%s' - ID %d already exists", ruleSetName, ruleSetID)
		}
		return nil
	})

	if err != nil {
		return fmt.Errorf("error querying rule sets: %v", err)
	}

	log.Printf("[Rules] Loaded %d Rule Sets", loadedCount)

	// Load global rule set
	err = ds.LoadGlobalRuleSet(ruleManager)
	if err != nil {
		return fmt.Errorf("error loading global rule set: %v", err)
	}

	ruleManager.stats.IncrementDatabaseOperations()
	return nil
}

// LoadRuleSetDetails loads the detailed rules for a specific rule set
// Converted from C++ WorldDatabase::LoadRuleSetDetails()
func (ds *DatabaseService) LoadRuleSetDetails(ruleManager *RuleManager, ruleSet *RuleSet) error {
	if ds.db == nil {
		return fmt.Errorf("database not initialized")
	}

	if ruleSet == nil {
		return fmt.Errorf("rule set is nil")
	}

	// Copy rules from global rule set (coded defaults) first
	ruleSet.CopyRulesInto(ruleManager.GetGlobalRuleSet())

	// Load rule overrides from database
	query := "SELECT rule_category, rule_type, rule_value FROM ruleset_details WHERE ruleset_id = ?"
	loadedRules := 0

	err := ds.db.Query(query, func(row *database.Row) error {
		categoryName := row.Text(0)
		typeName := row.Text(1)
		ruleValue := row.Text(2)

		// Find the rule by name
		rule := ruleSet.GetRuleByName(categoryName, typeName)
		if rule == nil {
			log.Printf("[Rules] Unknown rule with category '%s' and type '%s'", categoryName, typeName)
			return nil // Continue with other rules
		}

		log.Printf("[Rules] Setting rule category '%s', type '%s' to value: %s", categoryName, typeName, ruleValue)
		rule.SetValue(ruleValue)
		loadedRules++
		return nil
	}, ruleSet.GetID())

	if err != nil {
		return fmt.Errorf("error querying rule set details: %v", err)
	}

	log.Printf("[Rules] Loaded %d rule overrides for rule set '%s'", loadedRules, ruleSet.GetName())

	ruleManager.stats.IncrementDatabaseOperations()
	return nil
}

// SaveRuleSet saves a rule set to the database
func (ds *DatabaseService) SaveRuleSet(ruleSet *RuleSet) error {
	if ds.db == nil {
		return fmt.Errorf("database not initialized")
	}

	if ruleSet == nil {
		return fmt.Errorf("rule set is nil")
	}

	// Use transaction for atomicity
	return ds.db.Transaction(func(tx *database.DB) error {
		// Insert or update rule set
		query := `INSERT INTO rulesets (ruleset_id, ruleset_name, ruleset_active) 
		          VALUES (?, ?, 1) 
		          ON CONFLICT(ruleset_id) DO UPDATE SET 
		          ruleset_name = excluded.ruleset_name, 
		          ruleset_active = excluded.ruleset_active`

		err := tx.Exec(query, ruleSet.GetID(), ruleSet.GetName())
		if err != nil {
			return fmt.Errorf("error saving rule set: %v", err)
		}

		// Delete existing rule details
		err = tx.Exec("DELETE FROM ruleset_details WHERE ruleset_id = ?", ruleSet.GetID())
		if err != nil {
			return fmt.Errorf("error deleting existing rule details: %v", err)
		}

		// Insert rule details
		rules := ruleSet.GetRules()
		for _, categoryMap := range rules {
			for _, rule := range categoryMap {
				if rule.IsValid() {
					combined := rule.GetCombined()
					parts := splitCombined(combined)
					if len(parts) == 2 {
						query := "INSERT INTO ruleset_details (ruleset_id, rule_category, rule_type, rule_value) VALUES (?, ?, ?, ?)"
						err = tx.Exec(query, ruleSet.GetID(), parts[0], parts[1], rule.GetValue())
						if err != nil {
							return fmt.Errorf("error saving rule detail: %v", err)
						}
					}
				}
			}
		}

		return nil
	})
}

// DeleteRuleSet deletes a rule set from the database
func (ds *DatabaseService) DeleteRuleSet(ruleSetID int32) error {
	if ds.db == nil {
		return fmt.Errorf("database not initialized")
	}

	// Use transaction for atomicity
	return ds.db.Transaction(func(tx *database.DB) error {
		// Delete rule details first (foreign key constraint)
		err := tx.Exec("DELETE FROM ruleset_details WHERE ruleset_id = ?", ruleSetID)
		if err != nil {
			return fmt.Errorf("error deleting rule details: %v", err)
		}

		// Delete rule set
		err = tx.Exec("DELETE FROM rulesets WHERE ruleset_id = ?", ruleSetID)
		if err != nil {
			return fmt.Errorf("error deleting rule set: %v", err)
		}

		return nil
	})
}

// SetDefaultRuleSet sets the default rule set ID in the variables table
func (ds *DatabaseService) SetDefaultRuleSet(ruleSetID int32) error {
	if ds.db == nil {
		return fmt.Errorf("database not initialized")
	}

	query := `INSERT INTO variables (variable_name, variable_value, comment) 
	          VALUES (?, ?, 'Default ruleset ID') 
	          ON CONFLICT(variable_name) DO UPDATE SET 
	          variable_value = excluded.variable_value`

	err := ds.db.Exec(query, DefaultRuleSetIDVar, strconv.Itoa(int(ruleSetID)))
	if err != nil {
		return fmt.Errorf("error setting default rule set: %v", err)
	}

	return nil
}

// GetDefaultRuleSetID gets the default rule set ID from the variables table
func (ds *DatabaseService) GetDefaultRuleSetID() (int32, error) {
	if ds.db == nil {
		return 0, fmt.Errorf("database not initialized")
	}

	query := "SELECT variable_value FROM variables WHERE variable_name = ?"
	row, err := ds.db.QueryRow(query, DefaultRuleSetIDVar)
	if err != nil {
		return 0, fmt.Errorf("error querying default ruleset ID: %v", err)
	}

	if row == nil {
		return 0, fmt.Errorf("default ruleset ID not found in variables table")
	}
	defer row.Close()

	variableValue := row.Text(0)
	if id, err := strconv.ParseInt(variableValue, 10, 32); err == nil {
		return int32(id), nil
	}

	return 0, fmt.Errorf("invalid ruleset ID format: %s", variableValue)
}

// GetRuleSetList returns a list of all rule sets
func (ds *DatabaseService) GetRuleSetList() ([]RuleSetInfo, error) {
	if ds.db == nil {
		return nil, fmt.Errorf("database not initialized")
	}

	query := "SELECT ruleset_id, ruleset_name, ruleset_active FROM rulesets ORDER BY ruleset_id"
	var ruleSets []RuleSetInfo

	err := ds.db.Query(query, func(row *database.Row) error {
		info := RuleSetInfo{
			ID:     int32(row.Int64(0)),
			Name:   row.Text(1),
			Active: row.Bool(2),
		}
		ruleSets = append(ruleSets, info)
		return nil
	})

	if err != nil {
		return nil, fmt.Errorf("error querying rule sets: %v", err)
	}

	return ruleSets, nil
}

// ValidateDatabase ensures the required tables exist
func (ds *DatabaseService) ValidateDatabase() error {
	if ds.db == nil {
		return fmt.Errorf("database not initialized")
	}

	// Check if rulesets table exists
	query := "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='rulesets'"
	row, err := ds.db.QueryRow(query)
	if err != nil {
		return fmt.Errorf("error checking rulesets table: %v", err)
	}
	if row == nil || row.Int(0) == 0 {
		return fmt.Errorf("rulesets table does not exist")
	}
	row.Close()

	// Check if ruleset_details table exists
	query = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='ruleset_details'"
	row, err = ds.db.QueryRow(query)
	if err != nil {
		return fmt.Errorf("error checking ruleset_details table: %v", err)
	}
	if row == nil || row.Int(0) == 0 {
		return fmt.Errorf("ruleset_details table does not exist")
	}
	row.Close()

	// Check if variables table exists
	query = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='variables'"
	row, err = ds.db.QueryRow(query)
	if err != nil {
		return fmt.Errorf("error checking variables table: %v", err)
	}
	if row == nil || row.Int(0) == 0 {
		return fmt.Errorf("variables table does not exist")
	}
	row.Close()

	return nil
}

// RuleSetInfo contains basic information about a rule set
type RuleSetInfo struct {
	ID     int32  `json:"id"`
	Name   string `json:"name"`
	Active bool   `json:"active"`
}

// Helper function to split combined rule string
func splitCombined(combined string) []string {
	for i, char := range combined {
		if char == ':' {
			return []string{combined[:i], combined[i+1:]}
		}
	}
	return []string{combined}
}

// CreateRulesTables creates the necessary database tables for rules
func (ds *DatabaseService) CreateRulesTables() error {
	if ds.db == nil {
		return fmt.Errorf("database not initialized")
	}

	// Create rulesets table
	createRuleSets := `
	CREATE TABLE IF NOT EXISTS rulesets (
		ruleset_id INTEGER PRIMARY KEY,
		ruleset_name TEXT NOT NULL UNIQUE,
		ruleset_active INTEGER NOT NULL DEFAULT 0
	)`

	err := ds.db.Exec(createRuleSets)
	if err != nil {
		return fmt.Errorf("error creating rulesets table: %v", err)
	}

	// Create ruleset_details table
	createRuleSetDetails := `
	CREATE TABLE IF NOT EXISTS ruleset_details (
		id INTEGER PRIMARY KEY AUTOINCREMENT,
		ruleset_id INTEGER NOT NULL,
		rule_category TEXT NOT NULL,
		rule_type TEXT NOT NULL,
		rule_value TEXT NOT NULL,
		description TEXT,
		FOREIGN KEY (ruleset_id) REFERENCES rulesets(ruleset_id) ON DELETE CASCADE
	)`

	err = ds.db.Exec(createRuleSetDetails)
	if err != nil {
		return fmt.Errorf("error creating ruleset_details table: %v", err)
	}

	// Create variables table if it doesn't exist
	createVariables := `
	CREATE TABLE IF NOT EXISTS variables (
		variable_name TEXT PRIMARY KEY,
		variable_value TEXT NOT NULL,
		comment TEXT
	)`

	err = ds.db.Exec(createVariables)
	if err != nil {
		return fmt.Errorf("error creating variables table: %v", err)
	}

	// Create indexes for better performance
	indexes := []string{
		"CREATE INDEX IF NOT EXISTS idx_ruleset_details_ruleset_id ON ruleset_details(ruleset_id)",
		"CREATE INDEX IF NOT EXISTS idx_ruleset_details_category_type ON ruleset_details(rule_category, rule_type)",
		"CREATE INDEX IF NOT EXISTS idx_rulesets_active ON rulesets(ruleset_active)",
	}

	for _, indexSQL := range indexes {
		err = ds.db.Exec(indexSQL)
		if err != nil {
			return fmt.Errorf("error creating index: %v", err)
		}
	}

	return nil
}
