package handlers

import (
	"fmt"
	"log"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/patel-mann/poll-system/app/internal/models"
	"github.com/patel-mann/poll-system/app/internal/utils"
)

// SmartFilterQuery represents a parsed smart filter query
type SmartFilterQuery struct {
	Tables    []string
	Conditions []FilterCondition
	Groupings []string
	Aggregations []string
	Sorting   []string
	Limit     int
}

type FilterCondition struct {
	Table   string
	Column  string
	Operator string
	Value   interface{}
	LogicalOp string // AND, OR
}

// SmartFilterResult represents the result of a smart filter query
type SmartFilterResult struct {
	Columns []string
	Rows    [][]interface{}
	Query   string
	Count   int
	Error   string
}

// SmartFilterHandler handles intelligent filtering with natural language parsing
func SmartFilterHandler(w http.ResponseWriter, r *http.Request) {
	username, _ := models.GetCurrentUserName(r)
	role := r.Context().Value("user_role").(int)

	if role != 1 {
		http.Error(w, "Unauthorized", http.StatusForbidden)
		return
	}

	smartQuery := r.URL.Query().Get("smart_query")
	var result SmartFilterResult

	if smartQuery != "" {
		result = executeSmartFilter(smartQuery)
	}

	adminnav := role == 1
	volunteernav := role != 1

	utils.Render(w, "smart_reports.html", map[string]interface{}{
		"Title":           "Smart Reports & Analytics",
		"IsAuthenticated": true,
		"ShowAdminNav":    adminnav,
		"ShowVolunteerNav": volunteernav,
		"UserName":        username,
		"ActiveSection":   "reports",
		"SmartQuery":      smartQuery,
		"Result":          result,
		"QueryExamples":   getQueryExamples(),
	})
}

// executeSmartFilter parses and executes a smart filter query
func executeSmartFilter(query string) SmartFilterResult {
	parsed := parseSmartQuery(query)
	if parsed == nil {
		return SmartFilterResult{Error: "Could not parse query"}
	}

	sqlQuery, err := buildSQLFromParsed(parsed)
	if err != nil {
		return SmartFilterResult{Error: err.Error()}
	}

	return executeSQLQuery(sqlQuery)
}

// parseSmartQuery parses natural language into structured query
func parseSmartQuery(query string) *SmartFilterQuery {
	query = strings.ToLower(strings.TrimSpace(query))
	
	// Initialize the parsed query
	parsed := &SmartFilterQuery{
		Tables: []string{},
		Conditions: []FilterCondition{},
		Limit: 100, // Default limit
	}

	// Define entity mappings
	entityMappings := map[string]string{
		"volunteer":    "users",
		"volunteers":   "users", 
		"user":         "users",
		"users":        "users",
		"admin":        "users",
		"admins":       "users",
		"poll":         "poll",
		"polls":        "poll",
		"address":      "address_database",
		"addresses":    "address_database",
		"appointment":  "appointment",
		"appointments": "appointment",
		"team":         "team",
		"teams":        "team",
	}

	// Define column mappings for each table
	columnMappings := map[string]map[string]string{
		"users": {
			"name":       "first_name || ' ' || last_name",
			"email":      "email",
			"phone":      "phone",
			"role":       "role_id",
			"created":    "created_at",
			"updated":    "updated_at",
		},
		"poll": {
			"title":      "poll_title",
			"description": "poll_description",
			"active":     "is_active",
			"donated":    "amount_donated",
			"donation":   "amount_donated",
			"money":      "amount_donated",
			"amount":     "amount_donated",
			"created":    "created_at",
			"updated":    "updated_at",
		},
		"address_database": {
			"address":    "address",
			"street":     "street_name",
			"house":      "house_number",
			"visited":    "visited_validated",
			"latitude":   "latitude",
			"longitude":  "longitude",
			"created":    "created_at",
		},
		"appointment": {
			"date":       "appointment_date",
			"time":       "appointment_time",
			"created":    "created_at",
		},
		"team": {
			"lead":       "team_lead_id",
			"volunteer":  "volunteer_id",
			"created":    "created_at",
		},
	}

	_ = columnMappings

	// Extract entities from query
	for keyword, table := range entityMappings {
		if strings.Contains(query, keyword) {
			if !contains(parsed.Tables, table) {
				parsed.Tables = append(parsed.Tables, table)
			}
		}
	}

	// Parse conditions using regex patterns
	conditionPatterns := []struct {
		pattern string
		handler func(matches []string, parsed *SmartFilterQuery)
	}{
		// "volunteers who went to address X"
		{`(volunteer|user)s?\s+(?:who\s+)?(?:went\s+to|visited)\s+(?:address\s+)?(.+)`, func(matches []string, parsed *SmartFilterQuery) {
			addTablesIfNeeded(parsed, []string{"users", "appointment", "address_database"})
			parsed.Conditions = append(parsed.Conditions, FilterCondition{
				Table: "address_database", Column: "address", Operator: "ILIKE", Value: "%" + matches[2] + "%", LogicalOp: "AND",
			})
		}},
		
		// "poll responses for address X"
		{`poll\s+(?:response|responses|data)\s+(?:for|of|at)\s+(?:address\s+)?(.+)`, func(matches []string, parsed *SmartFilterQuery) {
			addTablesIfNeeded(parsed, []string{"poll", "address_database"})
			parsed.Conditions = append(parsed.Conditions, FilterCondition{
				Table: "address_database", Column: "address", Operator: "ILIKE", Value: "%" + matches[1] + "%", LogicalOp: "AND",
			})
		}},
		
		// "donations by volunteer X"
		{`(?:donation|donations|money|amount)\s+(?:by|from)\s+(?:volunteer\s+)?(.+)`, func(matches []string, parsed *SmartFilterQuery) {
			addTablesIfNeeded(parsed, []string{"poll", "users"})
			parsed.Conditions = append(parsed.Conditions, FilterCondition{
				Table: "users", Column: "first_name || ' ' || last_name", Operator: "ILIKE", Value: "%" + matches[1] + "%", LogicalOp: "AND",
			})
			parsed.Aggregations = append(parsed.Aggregations, "SUM(poll.amount_donated) as total_donated")
		}},
		
		// "team with most appointments"
		{`team\s+(?:with\s+)?(?:most|highest)\s+appointment`, func(matches []string, parsed *SmartFilterQuery) {
			addTablesIfNeeded(parsed, []string{"team", "appointment"})
			parsed.Aggregations = append(parsed.Aggregations, "COUNT(appointment.sched_id) as appointment_count")
			parsed.Groupings = append(parsed.Groupings, "team.team_id")
			parsed.Sorting = append(parsed.Sorting, "appointment_count DESC")
		}},
		
		// "people in team X"
		{`(?:people|members|users)\s+in\s+team\s+(\d+)`, func(matches []string, parsed *SmartFilterQuery) {
			addTablesIfNeeded(parsed, []string{"team", "users"})
			teamID, _ := strconv.Atoi(matches[1])
			parsed.Conditions = append(parsed.Conditions, FilterCondition{
				Table: "team", Column: "team_id", Operator: "=", Value: teamID, LogicalOp: "AND",
			})
		}},
		
		// "money made by team X"
		{`(?:money|donation|amount)\s+(?:made|earned)\s+by\s+team\s+(\d+)`, func(matches []string, parsed *SmartFilterQuery) {
			addTablesIfNeeded(parsed, []string{"team", "users", "poll"})
			teamID, _ := strconv.Atoi(matches[1])
			parsed.Conditions = append(parsed.Conditions, FilterCondition{
				Table: "team", Column: "team_id", Operator: "=", Value: teamID, LogicalOp: "AND",
			})
			parsed.Aggregations = append(parsed.Aggregations, "SUM(poll.amount_donated) as team_total")
		}},
		
		// "visited addresses"
		{`visited\s+address`, func(matches []string, parsed *SmartFilterQuery) {
			addTablesIfNeeded(parsed, []string{"address_database"})
			parsed.Conditions = append(parsed.Conditions, FilterCondition{
				Table: "address_database", Column: "visited_validated", Operator: "=", Value: true, LogicalOp: "AND",
			})
		}},
		
		// "active polls"
		{`active\s+poll`, func(matches []string, parsed *SmartFilterQuery) {
			addTablesIfNeeded(parsed, []string{"poll"})
			parsed.Conditions = append(parsed.Conditions, FilterCondition{
				Table: "poll", Column: "is_active", Operator: "=", Value: true, LogicalOp: "AND",
			})
		}},
		
		// Date filters
		{`(?:from|after)\s+(\d{4}-\d{2}-\d{2})`, func(matches []string, parsed *SmartFilterQuery) {
			for _, table := range parsed.Tables {
				parsed.Conditions = append(parsed.Conditions, FilterCondition{
					Table: table, Column: "created_at", Operator: ">=", Value: matches[1], LogicalOp: "AND",
				})
			}
		}},
		
		{`(?:to|before|until)\s+(\d{4}-\d{2}-\d{2})`, func(matches []string, parsed *SmartFilterQuery) {
			for _, table := range parsed.Tables {
				parsed.Conditions = append(parsed.Conditions, FilterCondition{
					Table: table, Column: "created_at", Operator: "<=", Value: matches[1] + " 23:59:59", LogicalOp: "AND",
				})
			}
		}},
	}

	// Apply pattern matching
	for _, pattern := range conditionPatterns {
		re := regexp.MustCompile(pattern.pattern)
		if matches := re.FindStringSubmatch(query); matches != nil {
			pattern.handler(matches, parsed)
		}
	}

	// If no tables were identified, return nil
	if len(parsed.Tables) == 0 {
		return nil
	}

	return parsed
}

// buildSQLFromParsed converts parsed query into SQL
func buildSQLFromParsed(parsed *SmartFilterQuery) (string, error) {
	var selectCols []string
	var fromClause []string
	var joinClauses []string
	var whereConditions []string
	var groupByClause string
	var orderByClause string

	// Define table aliases
	aliases := map[string]string{
		"users":            "u",
		"poll":             "p", 
		"address_database": "a",
		"appointment":      "ap",
		"team":             "t",
	}

	// Define default columns for each table
	defaultColumns := map[string][]string{
		"users": {"u.user_id", "u.first_name", "u.last_name", "u.email", "u.role_id", "u.created_at"},
		"poll": {"p.poll_id", "p.poll_title", "p.poll_description", "p.is_active", "p.amount_donated", "p.created_at"},
		"address_database": {"a.address_id", "a.address", "a.street_name", "a.visited_validated", "a.created_at"},
		"appointment": {"ap.sched_id", "ap.appointment_date", "ap.appointment_time", "ap.created_at"},
		"team": {"t.team_id", "t.team_lead_id", "t.volunteer_id", "t.created_at"},
	}

	// Build SELECT clause
	if len(parsed.Aggregations) > 0 {
		selectCols = parsed.Aggregations
		// Add grouping columns
		for _, table := range parsed.Tables {
			alias := aliases[table]
			if table == "users" {
				selectCols = append(selectCols, alias+".first_name || ' ' || "+alias+".last_name as user_name")
			} else if table == "team" {
				selectCols = append(selectCols, alias+".team_id")
			}
		}
	} else {
		// Include default columns for all tables
		for _, table := range parsed.Tables {
			selectCols = append(selectCols, defaultColumns[table]...)
		}
	}

	// Build FROM clause with main table
	mainTable := parsed.Tables[0]
	mainAlias := aliases[mainTable]
	fromClause = append(fromClause, mainTable+" "+mainAlias)

	// Build JOIN clauses for additional tables
	for i := 1; i < len(parsed.Tables); i++ {
		table := parsed.Tables[i]
		alias := aliases[table]
		
		// Define join conditions based on relationships
		joinCondition := getJoinCondition(mainTable, mainAlias, table, alias)
		if joinCondition != "" {
			joinClauses = append(joinClauses, "LEFT JOIN "+table+" "+alias+" ON "+joinCondition)
		}
	}

	// Build WHERE conditions
	for _, condition := range parsed.Conditions {
		alias := aliases[condition.Table]
		whereClause := fmt.Sprintf("%s.%s %s", alias, condition.Column, condition.Operator)
		
		switch v := condition.Value.(type) {
		case string:
			whereClause += fmt.Sprintf(" '%s'", strings.Replace(v, "'", "''", -1))
		case int:
			whereClause += fmt.Sprintf(" %d", v)
		case bool:
			whereClause += fmt.Sprintf(" %t", v)
		case float64:
			whereClause += fmt.Sprintf(" %.2f", v)
		}
		
		whereConditions = append(whereConditions, whereClause)
	}

	// Build GROUP BY
	if len(parsed.Groupings) > 0 {
		groupByClause = "GROUP BY " + strings.Join(parsed.Groupings, ", ")
	}

	// Build ORDER BY
	if len(parsed.Sorting) > 0 {
		orderByClause = "ORDER BY " + strings.Join(parsed.Sorting, ", ")
	} else {
		orderByClause = "ORDER BY " + mainAlias + ".created_at DESC"
	}

	// Construct final SQL
	sql := "SELECT " + strings.Join(selectCols, ", ")
	sql += " FROM " + strings.Join(fromClause, ", ")
	if len(joinClauses) > 0 {
		sql += " " + strings.Join(joinClauses, " ")
	}
	if len(whereConditions) > 0 {
		sql += " WHERE " + strings.Join(whereConditions, " AND ")
	}
	if groupByClause != "" {
		sql += " " + groupByClause
	}
	sql += " " + orderByClause
	sql += fmt.Sprintf(" LIMIT %d", parsed.Limit)

	return sql, nil
}

// getJoinCondition returns the appropriate JOIN condition between tables
func getJoinCondition(table1, alias1, table2, alias2 string) string {
	joinMap := map[string]map[string]string{
		"users": {
			"poll":             alias1 + ".user_id = " + alias2 + ".user_id",
			"appointment":      alias1 + ".user_id = " + alias2 + ".user_id",
			"team":            alias1 + ".user_id = " + alias2 + ".team_lead_id OR " + alias1 + ".user_id = " + alias2 + ".volunteer_id",
		},
		"poll": {
			"users":           alias2 + ".user_id = " + alias1 + ".user_id",
			"address_database": alias1 + ".address_id = " + alias2 + ".address_id",
		},
		"appointment": {
			"users":           alias2 + ".user_id = " + alias1 + ".user_id",
			"address_database": alias1 + ".address_id = " + alias2 + ".address_id",
		},
		"team": {
			"users":           alias2 + ".user_id = " + alias1 + ".team_lead_id OR " + alias2 + ".user_id = " + alias1 + ".volunteer_id",
		},
		"address_database": {
			"poll":        alias2 + ".address_id = " + alias1 + ".address_id",
			"appointment": alias2 + ".address_id = " + alias1 + ".address_id",
		},
	}

	if table1Joins, exists := joinMap[table1]; exists {
		if condition, exists := table1Joins[table2]; exists {
			return condition
		}
	}
	return ""
}

// executeSQLQuery executes the built SQL and returns results
func executeSQLQuery(sqlQuery string) SmartFilterResult {
	rows, err := models.DB.Query(sqlQuery)
	if err != nil {
		log.Println("Smart filter SQL error:", err)
		return SmartFilterResult{Error: "Query execution failed: " + err.Error(), Query: sqlQuery}
	}
	defer rows.Close()

	// Get column information
	columns, err := rows.Columns()
	if err != nil {
		return SmartFilterResult{Error: "Failed to get columns", Query: sqlQuery}
	}

	var results [][]interface{}
	for rows.Next() {
		// Create a slice of interface{} to hold the values
		values := make([]interface{}, len(columns))
		valuePtrs := make([]interface{}, len(columns))
		for i := range values {
			valuePtrs[i] = &values[i]
		}

		if err := rows.Scan(valuePtrs...); err != nil {
			log.Println("Error scanning row:", err)
			continue
		}

		// Convert values to strings for display
		row := make([]interface{}, len(columns))
		for i, val := range values {
			if val == nil {
				row[i] = ""
			} else {
				switch v := val.(type) {
				case []byte:
					row[i] = string(v)
				case time.Time:
					row[i] = v.Format("2006-01-02 15:04:05")
				default:
					row[i] = fmt.Sprintf("%v", v)
				}
			}
		}
		results = append(results, row)
	}

	return SmartFilterResult{
		Columns: columns,
		Rows:    results,
		Query:   sqlQuery,
		Count:   len(results),
	}
}

// Helper functions
func addTablesIfNeeded(parsed *SmartFilterQuery, tables []string) {
	for _, table := range tables {
		if !contains(parsed.Tables, table) {
			parsed.Tables = append(parsed.Tables, table)
		}
	}
}

func contains(slice []string, item string) bool {
	for _, s := range slice {
		if s == item {
			return true
		}
	}
	return false
}

// getQueryExamples returns example queries for the user
func getQueryExamples() []string {
	return []string{
		"volunteers who went to Main Street",
		"poll responses for 123 Oak Avenue", 
		"donations by volunteer John",
		"team with most appointments",
		"people in team 5",
		"money made by team 3",
		"visited addresses from 2024-01-01",
		"active polls created after 2024-06-01",
		"appointments for unvisited addresses",
		"users with role admin",
		"polls with donations over 100",
		"addresses visited by volunteer Sarah",
		"team leads with more than 5 appointments",
		"donations per address",
		"volunteer activity by month",
	}
}

// SmartFilterAPIHandler provides JSON API for smart filtering
func SmartFilterAPIHandler(w http.ResponseWriter, r *http.Request) {
	role := r.Context().Value("user_role").(int)
	if role != 1 {
		http.Error(w, "Unauthorized", http.StatusForbidden)
		return
	}

	smartQuery := r.URL.Query().Get("q")
	if smartQuery == "" {
		http.Error(w, "Query parameter 'q' is required", http.StatusBadRequest)
		return
	}

	result := executeSmartFilter(smartQuery)
	
	w.Header().Set("Content-Type", "application/json")
	// Convert result to JSON manually since we're not using json package
	response := fmt.Sprintf(`{
		"columns": [%s],
		"rows": [%s],
		"count": %d,
		"query": "%s",
		"error": "%s"
	}`, 
		formatColumnsForJSON(result.Columns),
		formatRowsForJSON(result.Rows),
		result.Count,
		strings.Replace(result.Query, "\"", "\\\"", -1),
		result.Error,
	)
	
	w.Write([]byte(response))
}

func formatColumnsForJSON(columns []string) string {
	var quoted []string
	for _, col := range columns {
		quoted = append(quoted, fmt.Sprintf(`"%s"`, col))
	}
	return strings.Join(quoted, ",")
}

func formatRowsForJSON(rows [][]interface{}) string {
	var rowStrings []string
	for _, row := range rows {
		var values []string
		for _, val := range row {
			values = append(values, fmt.Sprintf(`"%v"`, val))
		}
		rowStrings = append(rowStrings, "["+strings.Join(values, ",")+"]")
	}
	return strings.Join(rowStrings, ",")
}

// SmartFilterExportHandler exports smart filter results as CSV
func SmartFilterExportHandler(w http.ResponseWriter, r *http.Request) {
	role := r.Context().Value("user_role").(int)
	if role != 1 {
		http.Error(w, "Unauthorized", http.StatusForbidden)
		return
	}

	smartQuery := r.URL.Query().Get("smart_query")
	if smartQuery == "" {
		http.Error(w, "No query provided", http.StatusBadRequest)
		return
	}

	result := executeSmartFilter(smartQuery)
	if result.Error != "" {
		http.Error(w, result.Error, http.StatusBadRequest)
		return
	}

	w.Header().Set("Content-Type", "text/csv")
	w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"smart_filter_results_%s.csv\"", time.Now().Format("2006-01-02")))

	// Write CSV headers
	w.Write([]byte(strings.Join(result.Columns, ",") + "\n"))

	// Write CSV rows
	for _, row := range result.Rows {
		var csvRow []string
		for _, val := range row {
			// Escape commas and quotes in CSV values
			strVal := fmt.Sprintf("%v", val)
			if strings.Contains(strVal, ",") || strings.Contains(strVal, "\"") {
				strVal = "\"" + strings.Replace(strVal, "\"", "\"\"", -1) + "\""
			}
			csvRow = append(csvRow, strVal)
		}
		w.Write([]byte(strings.Join(csvRow, ",") + "\n"))
	}
}