package main

import (
	"context"
	"database/sql"
	"errors"
	"net/http"
	"time"

	"torrentGateway/internal/auth"
	"torrentGateway/internal/middleware"
)

// Common auth errors for testing
var (
	ErrInvalidSession = errors.New("invalid or expired session")
	ErrUserNotFound   = errors.New("user not found")
)

// MockAuth provides authentication bypass for testing
type MockAuth struct {
	testPubkey string
	isAdmin    bool
}

// NewMockAuth creates a new mock authentication system
func NewMockAuth(testPubkey string, isAdmin bool) *MockAuth {
	return &MockAuth{
		testPubkey: testPubkey,
		isAdmin:    isAdmin,
	}
}

// GetTestSessionToken returns a mock session token for testing
func (m *MockAuth) GetTestSessionToken() string {
	return "test_session_token_" + m.testPubkey
}

// CreateTestMiddleware creates middleware that bypasses auth for testing
func (m *MockAuth) CreateTestMiddleware() func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// Add test user to context
			ctx := context.WithValue(r.Context(), middleware.UserKey, m.testPubkey)
			next.ServeHTTP(w, r.WithContext(ctx))
		})
	}
}

// MockNostrAuth implements the auth.NostrAuth interface for testing
type MockNostrAuth struct {
	db         *sql.DB
	testPubkey string
	isAdmin    bool
}

// NewMockNostrAuth creates a mock NostrAuth for testing
func NewMockNostrAuth(db *sql.DB, testPubkey string, isAdmin bool) *MockNostrAuth {
	return &MockNostrAuth{
		db:         db,
		testPubkey: testPubkey,
		isAdmin:    isAdmin,
	}
}

// ValidateNIP07 always returns the test pubkey for testing
func (m *MockNostrAuth) ValidateNIP07(authEvent string) (string, error) {
	return m.testPubkey, nil
}

// ValidateNIP46 always returns the test pubkey for testing
func (m *MockNostrAuth) ValidateNIP46(bunkerURL string) (string, error) {
	return m.testPubkey, nil
}

// CreateSession creates a mock session
func (m *MockNostrAuth) CreateSession(pubkey string) (*auth.Session, error) {
	return &auth.Session{
		Token:     "test_session_token_" + pubkey,
		Pubkey:    pubkey,
		CreatedAt: time.Now(),
		ExpiresAt: time.Now().Add(24 * time.Hour),
	}, nil
}

// ValidateSession validates mock sessions
func (m *MockNostrAuth) ValidateSession(token string) (string, error) {
	if token == "test_session_token_"+m.testPubkey {
		return m.testPubkey, nil
	}
	return "", ErrInvalidSession
}

// GetUser returns mock user data
func (m *MockNostrAuth) GetUser(pubkey string) (*auth.User, error) {
	return &auth.User{
		Pubkey:    pubkey,
		LastLogin: time.Now(),
	}, nil
}

// IsAdmin returns the mock admin status
func (m *MockNostrAuth) IsAdmin(pubkey string) bool {
	return m.isAdmin && pubkey == m.testPubkey
}

// UpdateUserStats is a no-op for testing
func (m *MockNostrAuth) UpdateUserStats(pubkey string, storageUsed int64, fileCount int) error {
	return nil
}

// RevokeSession revokes a session (no-op for testing)
func (m *MockNostrAuth) RevokeSession(token string) error {
	return nil
}

// CleanExpiredSessions cleans expired sessions (no-op for testing)
func (m *MockNostrAuth) CleanExpiredSessions() error {
	return nil
}

// UpdateUserProfile updates user profile (no-op for testing)
func (m *MockNostrAuth) UpdateUserProfile(pubkey, displayName, profileImage string) error {
	return nil
}

// CreateTestUser creates a test user in the database
func (m *MockNostrAuth) CreateTestUser() error {
	_, err := m.db.Exec(`
		INSERT OR IGNORE INTO users (pubkey, storage_used, file_count, last_login, created_at)
		VALUES (?, 0, 0, ?, ?)
	`, m.testPubkey, time.Now(), time.Now())
	return err
}