package testutils

import (
	"crypto/rand"
	"fmt"
	"math/big"
	"testing"

	"goyco/internal/database"

	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
)

type TestConfig struct {
	Database  DatabaseConfig  `json:"database"`
	Server    ServerConfig    `json:"server"`
	JWT       JWTConfig       `json:"jwt"`
	Email     EmailConfig     `json:"email"`
	RateLimit RateLimitConfig `json:"rate_limit"`
	Cache     CacheConfig     `json:"cache"`
	Security  SecurityConfig  `json:"security"`
	Logging   LoggingConfig   `json:"logging"`
}

type DatabaseConfig struct {
	Driver   string `json:"driver"`
	Host     string `json:"host"`
	Port     int    `json:"port"`
	User     string `json:"user"`
	Password string `json:"password"`
	DBName   string `json:"dbname"`
	SSLMode  string `json:"sslmode"`
}

type ServerConfig struct {
	Host string `json:"host"`
	Port int    `json:"port"`
}

type JWTConfig struct {
	Secret     string `json:"secret"`
	Expiration int    `json:"expiration"`
}

type EmailConfig struct {
	SMTPHost     string `json:"smtp_host"`
	SMTPPort     int    `json:"smtp_port"`
	SMTPUsername string `json:"smtp_username"`
	SMTPPassword string `json:"smtp_password"`
	FromEmail    string `json:"from_email"`
	FromName     string `json:"from_name"`
}

type RateLimitConfig struct {
	RequestsPerMinute int `json:"requests_per_minute"`
	BurstSize         int `json:"burst_size"`
}

type CacheConfig struct {
	Type string `json:"type"`
}

type SecurityConfig struct {
	EnableCSRF        bool     `json:"enable_csrf"`
	CSRFSecret        string   `json:"csrf_secret"`
	EnableCORS        bool     `json:"enable_cors"`
	AllowedOrigins    []string `json:"allowed_origins"`
	EnableRateLimit   bool     `json:"enable_rate_limit"`
	EnableCompression bool     `json:"enable_compression"`
}

type LoggingConfig struct {
	Level  string `json:"level"`
	Format string `json:"format"`
	Output string `json:"output"`
}

type TestFixtures struct {
	Users  []*database.User
	Posts  []*database.Post
	Votes  []*database.Vote
	Config *TestConfig
}

func NewTestFixtures(t *testing.T) *TestFixtures {
	t.Helper()

	return &TestFixtures{
		Users: []*database.User{
			{
				Username:      "testuser1",
				Email:         "user1@test.local",
				Password:      "SecurePass123!",
				EmailVerified: true,
			},
			{
				Username:      "testuser2",
				Email:         "user2@test.local",
				Password:      "SecurePass456!",
				EmailVerified: true,
			},
			{
				Username:      "unverified_user",
				Email:         "unverified@test.local",
				Password:      "SecurePass789!",
				EmailVerified: false,
			},
		},
		Posts: []*database.Post{
			{
				Title:     "Test Post 1",
				URL:       "https://example.com/post1",
				Content:   "This is test content for post 1",
				UpVotes:   5,
				DownVotes: 1,
				Score:     4,
			},
			{
				Title:     "Test Post 2",
				URL:       "https://example.com/post2",
				Content:   "This is test content for post 2",
				UpVotes:   3,
				DownVotes: 0,
				Score:     3,
			},
		},
		Votes: []*database.Vote{
			{
				Type: database.VoteUp,
			},
			{
				Type: database.VoteDown,
			},
			{
				Type: database.VoteNone,
			},
		},
		Config: &TestConfig{
			Database: DatabaseConfig{
				Driver:   "sqlite",
				Host:     ":memory:",
				Port:     0,
				User:     "",
				Password: "",
				DBName:   "test",
				SSLMode:  "disable",
			},
			Server: ServerConfig{
				Host: "localhost",
				Port: 8080,
			},
			JWT: JWTConfig{
				Secret:     "test-secret-key",
				Expiration: 24,
			},
			Email: EmailConfig{
				SMTPHost:     "localhost",
				SMTPPort:     587,
				SMTPUsername: "test@example.com",
				SMTPPassword: "test-password",
				FromEmail:    "test@example.com",
				FromName:     "Test App",
			},
			RateLimit: RateLimitConfig{
				RequestsPerMinute: 60,
				BurstSize:         10,
			},
			Cache: CacheConfig{
				Type: "memory",
			},
			Security: SecurityConfig{
				EnableCSRF:        true,
				CSRFSecret:        "test-csrf-secret",
				EnableCORS:        true,
				AllowedOrigins:    []string{"http://localhost:3000"},
				EnableRateLimit:   true,
				EnableCompression: true,
			},
			Logging: LoggingConfig{
				Level:  "debug",
				Format: "json",
				Output: "stdout",
			},
		},
	}
}

func CreateSecureTestUser(t *testing.T, db *gorm.DB, username, email string) *database.User {
	t.Helper()

	if username == "" {
		username = generateSecureRandomString(8)
	}
	if email == "" {
		email = fmt.Sprintf("%s@test.local", username)
	}

	password := generateSecurePassword()
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		t.Fatalf("Failed to hash password: %v", err)
	}

	user := &database.User{
		Username:      username,
		Email:         email,
		Password:      string(hashedPassword),
		EmailVerified: true,
	}

	if err := db.Create(user).Error; err != nil {
		t.Fatalf("Failed to create test user: %v", err)
	}

	return user
}

func CreateSecureTestPost(t *testing.T, db *gorm.DB, authorID uint) *database.Post {
	t.Helper()

	title := generateSecureRandomString(12)
	url := fmt.Sprintf("https://example.com/%s", generateSecureRandomString(8))
	content := fmt.Sprintf("Test content for %s", title)

	post := &database.Post{
		Title:     title,
		URL:       url,
		Content:   content,
		AuthorID:  &authorID,
		UpVotes:   0,
		DownVotes: 0,
		Score:     0,
	}

	if err := db.Create(post).Error; err != nil {
		t.Fatalf("Failed to create test post: %v", err)
	}

	return post
}

func CreateSecureTestVote(t *testing.T, db *gorm.DB, userID, postID uint, voteType database.VoteType) *database.Vote {
	t.Helper()

	vote := &database.Vote{
		UserID: &userID,
		PostID: postID,
		Type:   voteType,
	}

	if err := db.Create(vote).Error; err != nil {
		t.Fatalf("Failed to create test vote: %v", err)
	}

	return vote
}

func (f *TestFixtures) CreateTestUsers(t *testing.T, db *gorm.DB) []*database.User {
	t.Helper()

	var users []*database.User
	for _, userData := range f.Users {
		user := *userData

		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
		if err != nil {
			t.Fatalf("Failed to hash password: %v", err)
		}
		user.Password = string(hashedPassword)

		if err := db.Create(&user).Error; err != nil {
			t.Fatalf("Failed to create test user: %v", err)
		}
		users = append(users, &user)
	}

	return users
}

func (f *TestFixtures) CreateTestPosts(t *testing.T, db *gorm.DB, authorID uint) []*database.Post {
	t.Helper()

	var posts []*database.Post
	for _, postData := range f.Posts {
		post := *postData
		post.AuthorID = &authorID

		if err := db.Create(&post).Error; err != nil {
			t.Fatalf("Failed to create test post: %v", err)
		}
		posts = append(posts, &post)
	}

	return posts
}

func (f *TestFixtures) CreateTestVotes(t *testing.T, db *gorm.DB, userID, postID uint) []*database.Vote {
	t.Helper()

	var votes []*database.Vote
	for _, voteData := range f.Votes {
		vote := *voteData
		vote.UserID = &userID
		vote.PostID = postID

		if err := db.Create(&vote).Error; err != nil {
			t.Fatalf("Failed to create test vote: %v", err)
		}
		votes = append(votes, &vote)
	}

	return votes
}

func generateSecureRandomString(length int) string {
	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	result := make([]byte, length)

	for i := range result {
		num, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
		result[i] = charset[num.Int64()]
	}

	return string(result)
}

func generateSecurePassword() string {
	letters := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	numbers := "0123456789"
	special := "!@#$%^&*"

	password := ""

	num, _ := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
	password += string(letters[num.Int64()])

	num, _ = rand.Int(rand.Reader, big.NewInt(int64(len(numbers))))
	password += string(numbers[num.Int64()])

	num, _ = rand.Int(rand.Reader, big.NewInt(int64(len(special))))
	password += string(special[num.Int64()])

	for len(password) < 12 {
		charset := letters + numbers + special
		num, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
		password += string(charset[num.Int64()])
	}

	return password
}

func CleanupTestData(t *testing.T, db *gorm.DB) {
	t.Helper()

	if err := db.Exec("DELETE FROM votes").Error; err != nil {
		t.Logf("Warning: Failed to clean up votes: %v", err)
	}
	if err := db.Exec("DELETE FROM posts").Error; err != nil {
		t.Logf("Warning: Failed to clean up posts: %v", err)
	}
	if err := db.Exec("DELETE FROM account_deletion_requests").Error; err != nil {
		t.Logf("Warning: Failed to clean up account deletion requests: %v", err)
	}
	if err := db.Exec("DELETE FROM refresh_tokens").Error; err != nil {
		t.Logf("Warning: Failed to clean up refresh tokens: %v", err)
	}
	if err := db.Exec("DELETE FROM users").Error; err != nil {
		t.Logf("Warning: Failed to clean up users: %v", err)
	}
}
