package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"time"
)

type User struct {
	ID        int       `json:"id"`
	Name      string    `json:"name"`
	Email     string    `json:"email"`
	CreatedAt time.Time `json:"created_at"`
}

type HealthResponse struct {
	Status       string    `json:"status"`
	Timestamp    time.Time `json:"timestamp"`
	Service      string    `json:"service"`
	DatabaseName string    `json:"database_name,omitempty"`
	GatewayURL   string    `json:"gateway_url,omitempty"`
}

type UsersResponse struct {
	Users []User `json:"users"`
	Total int    `json:"total"`
}

type CreateUserRequest struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

// In-memory storage (used when DATABASE_NAME is not set)
var users = []User{
	{ID: 1, Name: "Alice", Email: "alice@example.com", CreatedAt: time.Now()},
	{ID: 2, Name: "Bob", Email: "bob@example.com", CreatedAt: time.Now()},
	{ID: 3, Name: "Charlie", Email: "charlie@example.com", CreatedAt: time.Now()},
}
var nextID = 4

// Environment variables
var (
	databaseName = os.Getenv("DATABASE_NAME")
	gatewayURL   = os.Getenv("GATEWAY_URL")
	apiKey       = os.Getenv("API_KEY")
)

// executeSQL executes a SQL query against the hosted SQLite database
func executeSQL(query string, args ...interface{}) ([]map[string]interface{}, error) {
	if databaseName == "" || gatewayURL == "" {
		return nil, fmt.Errorf("database not configured")
	}

	// Build the query with parameters
	// Gateway expects: database_name, query (not sql), params
	reqBody := map[string]interface{}{
		"database_name": databaseName,
		"query":         query,
		"params":        args,
	}
	bodyBytes, _ := json.Marshal(reqBody)

	// Gateway endpoint is /v1/db/sqlite/query (not /v1/db/{name}/query)
	url := fmt.Sprintf("%s/v1/db/sqlite/query", gatewayURL)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(bodyBytes))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")
	if apiKey != "" {
		req.Header.Set("Authorization", "Bearer "+apiKey)
	}

	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("database error: %s", string(body))
	}

	// Gateway returns: columns []string, rows [][]interface{}
	// We need to convert rows to []map[string]interface{}
	var result struct {
		Rows    [][]interface{} `json:"rows"`
		Columns []string        `json:"columns"`
		Error   string          `json:"error,omitempty"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, err
	}

	if result.Error != "" {
		return nil, fmt.Errorf("query error: %s", result.Error)
	}

	// Convert [][]interface{} to []map[string]interface{}
	rows := make([]map[string]interface{}, 0, len(result.Rows))
	for _, row := range result.Rows {
		rowMap := make(map[string]interface{})
		for i, col := range result.Columns {
			if i < len(row) {
				rowMap[col] = row[i]
			}
		}
		rows = append(rows, rowMap)
	}

	return rows, nil
}

// initDatabase creates the users table if it doesn't exist
func initDatabase() error {
	if databaseName == "" || gatewayURL == "" {
		log.Printf("DATABASE_NAME or GATEWAY_URL not set, using in-memory storage")
		return nil
	}

	query := `CREATE TABLE IF NOT EXISTS users (
		id INTEGER PRIMARY KEY AUTOINCREMENT,
		name TEXT NOT NULL,
		email TEXT NOT NULL,
		created_at DATETIME DEFAULT CURRENT_TIMESTAMP
	)`

	_, err := executeSQL(query)
	if err != nil {
		// Log but don't fail - the table might already exist
		log.Printf("Warning: Could not create users table: %v", err)
	} else {
		log.Printf("Users table initialized")
	}
	return nil
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(HealthResponse{
		Status:       "healthy",
		Timestamp:    time.Now(),
		Service:      "go-backend-test",
		DatabaseName: databaseName,
		GatewayURL:   gatewayURL,
	})
}

func usersHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	// Check if database is configured
	useDatabase := databaseName != "" && gatewayURL != ""

	switch r.Method {
	case http.MethodGet:
		if useDatabase {
			// Query from hosted SQLite
			rows, err := executeSQL("SELECT id, name, email, created_at FROM users ORDER BY id")
			if err != nil {
				log.Printf("Database query error: %v", err)
				http.Error(w, "Database error", http.StatusInternalServerError)
				return
			}

			var dbUsers []User
			for _, row := range rows {
				user := User{
					ID:    int(row["id"].(float64)),
					Name:  row["name"].(string),
					Email: row["email"].(string),
				}
				if ct, ok := row["created_at"].(string); ok {
					user.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", ct)
				}
				dbUsers = append(dbUsers, user)
			}

			json.NewEncoder(w).Encode(UsersResponse{
				Users: dbUsers,
				Total: len(dbUsers),
			})
		} else {
			// Use in-memory storage
			json.NewEncoder(w).Encode(UsersResponse{
				Users: users,
				Total: len(users),
			})
		}

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

		if req.Name == "" || req.Email == "" {
			http.Error(w, "Name and email are required", http.StatusBadRequest)
			return
		}

		if useDatabase {
			// Insert into hosted SQLite
			_, err := executeSQL("INSERT INTO users (name, email) VALUES (?, ?)", req.Name, req.Email)
			if err != nil {
				log.Printf("Database insert error: %v", err)
				http.Error(w, "Database error", http.StatusInternalServerError)
				return
			}

			// Get the inserted user (last insert ID)
			rows, err := executeSQL("SELECT id, name, email, created_at FROM users WHERE name = ? AND email = ? ORDER BY id DESC LIMIT 1", req.Name, req.Email)
			if err != nil || len(rows) == 0 {
				http.Error(w, "Failed to retrieve created user", http.StatusInternalServerError)
				return
			}

			newUser := User{
				ID:    int(rows[0]["id"].(float64)),
				Name:  rows[0]["name"].(string),
				Email: rows[0]["email"].(string),
			}
			if ct, ok := rows[0]["created_at"].(string); ok {
				newUser.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", ct)
			}

			w.WriteHeader(http.StatusCreated)
			json.NewEncoder(w).Encode(map[string]interface{}{
				"success": true,
				"user":    newUser,
			})
		} else {
			// Use in-memory storage
			newUser := User{
				ID:        nextID,
				Name:      req.Name,
				Email:     req.Email,
				CreatedAt: time.Now(),
			}
			nextID++
			users = append(users, newUser)

			w.WriteHeader(http.StatusCreated)
			json.NewEncoder(w).Encode(map[string]interface{}{
				"success": true,
				"user":    newUser,
			})
		}

	case http.MethodDelete:
		// Parse user ID from query string (e.g., /api/users?id=1)
		idStr := r.URL.Query().Get("id")
		if idStr == "" {
			http.Error(w, "User ID required", http.StatusBadRequest)
			return
		}

		var id int
		fmt.Sscanf(idStr, "%d", &id)

		if useDatabase {
			_, err := executeSQL("DELETE FROM users WHERE id = ?", id)
			if err != nil {
				log.Printf("Database delete error: %v", err)
				http.Error(w, "Database error", http.StatusInternalServerError)
				return
			}
		} else {
			// Delete from in-memory storage
			for i, u := range users {
				if u.ID == id {
					users = append(users[:i], users[i+1:]...)
					break
				}
			}
		}

		json.NewEncoder(w).Encode(map[string]interface{}{
			"success": true,
			"message": "User deleted",
		})

	default:
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
	}
}

func rootHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")

	storageType := "in-memory"
	if databaseName != "" && gatewayURL != "" {
		storageType = "hosted-sqlite"
	}

	json.NewEncoder(w).Encode(map[string]interface{}{
		"message": "Orama Network Go Backend Test",
		"version": "1.0.0",
		"storage": storageType,
		"endpoints": map[string]string{
			"health": "GET /health",
			"users":  "GET/POST/DELETE /api/users",
		},
		"config": map[string]string{
			"database_name": maskIfSet(databaseName),
			"gateway_url":   maskIfSet(gatewayURL),
		},
	})
}

func maskIfSet(s string) string {
	if s == "" {
		return "[not configured]"
	}
	if strings.Contains(s, "://") {
		// Mask URL partially
		return s[:strings.Index(s, "://")+3] + "..."
	}
	return "[configured]"
}

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	// Initialize database if configured
	if err := initDatabase(); err != nil {
		log.Printf("Warning: Database initialization failed: %v", err)
	}

	http.HandleFunc("/", rootHandler)
	http.HandleFunc("/health", healthHandler)
	http.HandleFunc("/api/users", usersHandler)

	log.Printf("Starting Go backend on port %s", port)
	log.Printf("Database: %s", maskIfSet(databaseName))
	log.Printf("Gateway: %s", maskIfSet(gatewayURL))
	log.Fatal(http.ListenAndServe(":"+port, nil))
}
