package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgxpool"
	"github.com/joho/godotenv"
	"golang.org/x/crypto/bcrypt"
)

func main() {
	log.Println("Starting seeder...")
	if err := godotenv.Load(); err != nil {
		log.Println("No .env file found, relying on env vars")
	}

	dbURL := os.Getenv("DATABASE_URL")
	if dbURL == "" {
		log.Fatal("DATABASE_URL is not set")
	}

	pool, err := pgxpool.New(context.Background(), dbURL)
	if err != nil {
		log.Fatalf("Unable to connect to database: %v", err)
	}
	defer pool.Close()

	ctx := context.Background()

	// 1. Fix Schema (Missing Columns)
	log.Println("Fixing schema...")
	schemaFixes := []string{
		`ALTER TABLE categories ADD COLUMN IF NOT EXISTS is_active BOOLEAN DEFAULT TRUE`,
		`ALTER TABLE categories ADD COLUMN IF NOT EXISTS icon_url TEXT`,
		`ALTER TABLE categories ADD COLUMN IF NOT EXISTS official_account_id UUID`,
		`ALTER TABLE categories ADD COLUMN IF NOT EXISTS slug TEXT UNIQUE`,
		`CREATE TABLE IF NOT EXISTS user_category_settings (
            user_id UUID NOT NULL,
            category_id UUID NOT NULL,
            enabled BOOLEAN NOT NULL DEFAULT TRUE,
            updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
            PRIMARY KEY (user_id, category_id)
        )`,
	}

	for _, query := range schemaFixes {
		_, err := pool.Exec(ctx, query)
		if err != nil {
			log.Printf("Warning executing schema fix: %s\nError: %v", query, err)
			// Continue as column might exist or other non-fatal error
		}
	}

	// 2. Seed Categories
	log.Println("Seeding categories...")
	categories := []struct {
		Name        string
		Slug        string
		Description string
		IconURL     string
	}{
		{"Travel", "travel", "Explore the world", "✈️"},
		{"Food", "food", "Delicious eats", "🍔"},
		{"Tech", "tech", "Latest gadgets and software", "💻"},
		{"Art", "art", "Creative expressions", "🎨"},
		{"Music", "music", "Sounds and rhythms", "🎵"},
	}

	for _, cat := range categories {
		var catID uuid.UUID
		// Check if exists
		err := pool.QueryRow(ctx, "SELECT id FROM categories WHERE slug = $1", cat.Slug).Scan(&catID)
		if err == pgx.ErrNoRows {
			// Create
			err = pool.QueryRow(ctx, `
                INSERT INTO categories (name, slug, description, icon_url, is_active)
                VALUES ($1, $2, $3, $4, true)
                RETURNING id
            `, cat.Name, cat.Slug, cat.Description, cat.IconURL).Scan(&catID)
			if err != nil {
				log.Printf("Failed to create category %s: %v", cat.Name, err)
				continue
			}
			log.Printf("Created category: %s", cat.Name)
		} else if err != nil {
			log.Printf("Error checking category %s: %v", cat.Name, err)
			continue
		} else {
			// Update icon/active
			_, err = pool.Exec(ctx, "UPDATE categories SET icon_url = $1, is_active = true WHERE id = $2", cat.IconURL, catID)
			if err != nil {
				log.Printf("Failed to update category %s: %v", cat.Name, err)
			}
		}

		// 3. Create/Link Official Account
		if err := ensureOfficialAccount(ctx, pool, cat.Name, catID); err != nil {
			log.Printf("Failed to ensure official account for %s: %v", cat.Name, err)
		}
	}

	// 4. Generate Random Users & Posts (Stress Test Data)
	log.Println("Generating stress test data (50 users, 200 posts)...")

	commonPasswordsBytes, _ := bcrypt.GenerateFromPassword([]byte("password123"), bcrypt.DefaultCost)
	commonPasswordHash := string(commonPasswordsBytes)

	for i := 0; i < 50; i++ {
		email := fmt.Sprintf("user_%d@stress.test", i)
		handle := fmt.Sprintf("user_%d", i)
		displayName := fmt.Sprintf("Stress User %d", i)

		// Create User
		var userID uuid.UUID
		err := pool.QueryRow(ctx, "SELECT id FROM users WHERE email = $1", email).Scan(&userID)
		if err == pgx.ErrNoRows {
			err = pool.QueryRow(ctx, `
				INSERT INTO users (email, encrypted_password, created_at, updated_at)
				VALUES ($1, $2, NOW(), NOW())
				RETURNING id
			`, email, commonPasswordHash).Scan(&userID)
			if err != nil {
				log.Printf("Failed to create user %s: %v", email, err)
				continue
			}

			// Create Profile
			_, err = pool.Exec(ctx, `
				INSERT INTO profiles (id, handle, display_name, created_at, updated_at)
				VALUES ($1, $2, $3, NOW(), NOW())
			`, userID, handle, displayName)

			// Initialize Trust State
			pool.Exec(ctx, "INSERT INTO trust_state (user_id) VALUES ($1)", userID)

			// Initialize Privacy Settings
			pool.Exec(ctx, "INSERT INTO profile_privacy_settings (user_id) VALUES ($1)", userID)
		} else if err != nil {
			continue
		}

		// Create 4-5 Posts per user
		for j := 0; j < 5; j++ {
			body := fmt.Sprintf("Stress test post #%d from user %d. #stress #test", j, i)
			_, err := pool.Exec(ctx, `
				INSERT INTO posts (author_id, body, status, visibility, created_at)
				VALUES ($1, $2, 'active', 'public', NOW())
			`, userID, body)
			if err != nil {
				log.Printf("Failed to create post for %s: %v", handle, err)
			}
		}
	}

	log.Println("Seeding complete.")
}

func ensureOfficialAccount(ctx context.Context, pool *pgxpool.Pool, catName string, catID uuid.UUID) error {
	// Check if category has official account
	var officialAccountID *uuid.UUID
	err := pool.QueryRow(ctx, "SELECT official_account_id FROM categories WHERE id = $1", catID).Scan(&officialAccountID)
	if err != nil {
		return err
	}

	if officialAccountID != nil {
		// Exists, ensure profile is correct?
		return nil // Assume good
	}

	// Create User
	email := fmt.Sprintf("official_%s@sojorn.com", catName)
	handle := fmt.Sprintf("sojorn_%s", catName)
	displayName := fmt.Sprintf("Sojorn %s", catName)
	password := "OfficialAccount123!" // Should change later

	hashedBytes, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	passwordHash := string(hashedBytes)

	var userID uuid.UUID
	// Check user exists
	err = pool.QueryRow(ctx, "SELECT id FROM users WHERE email = $1", email).Scan(&userID)
	if err == pgx.ErrNoRows {
		err = pool.QueryRow(ctx, `
            INSERT INTO users (email, encrypted_password, created_at, updated_at)
            VALUES ($1, $2, NOW(), NOW())
            RETURNING id
        `, email, passwordHash).Scan(&userID)
		if err != nil {
			return fmt.Errorf("creating user: %w", err)
		}
	} else if err != nil {
		return fmt.Errorf("checking user: %w", err)
	}

	// Check/Create Profile
	var profileID uuid.UUID
	err = pool.QueryRow(ctx, "SELECT id FROM profiles WHERE id = $1", userID).Scan(&profileID)
	if err == pgx.ErrNoRows {
		_, err = pool.Exec(ctx, `
            INSERT INTO profiles (id, handle, display_name, is_official, created_at, updated_at)
            VALUES ($1, $2, $3, true, NOW(), NOW())
        `, userID, handle, displayName)
		if err != nil {
			return fmt.Errorf("creating profile: %w", err)
		}
	} else if err != nil {
		return fmt.Errorf("checking profile: %w", err)
	}

	// Update Category
	_, err = pool.Exec(ctx, "UPDATE categories SET official_account_id = $1 WHERE id = $2", userID, catID)
	if err != nil {
		return fmt.Errorf("linking category: %w", err)
	}

	// Create Seed Posts
	for i := 1; i <= 5; i++ {
		body := fmt.Sprintf("Welcome to the official %s feed! Post #%d. #%s", catName, i, catName)
		_, err := pool.Exec(ctx, `
            INSERT INTO posts (author_id, category_id, body, status, visibility, created_at)
            VALUES ($1, $2, $3, 'active', 'public', NOW())
         `, userID, catID, body)
		if err != nil {
			log.Printf("Failed to seed post for %s: %v", catName, err)
		}
	}

	log.Printf("Official account setup content for %s", catName)
	return nil
}
