package admin

import (
	"database/sql"
	"fmt"
	"net/http"
	"strings"
	"time"

	"torrentGateway/internal/auth"
)

// AdminAuth handles admin authentication and authorization
type AdminAuth struct {
	adminPubkeys map[string]bool
	nostrAuth    *auth.NostrAuth
	db           *sql.DB
}

// NewAdminAuth creates a new admin authentication handler
func NewAdminAuth(adminPubkeys []string, nostrAuth *auth.NostrAuth, db *sql.DB) *AdminAuth {
	pubkeyMap := make(map[string]bool)
	for _, pubkey := range adminPubkeys {
		pubkeyMap[pubkey] = true
	}

	return &AdminAuth{
		adminPubkeys: pubkeyMap,
		nostrAuth:    nostrAuth,
		db:           db,
	}
}

// IsAdmin checks if a pubkey belongs to an admin
func (aa *AdminAuth) IsAdmin(pubkey string) bool {
	return aa.adminPubkeys[pubkey]
}

// ValidateAdminRequest validates that the request comes from an authenticated admin
func (aa *AdminAuth) ValidateAdminRequest(r *http.Request) (string, error) {
	// Extract session token from header or cookie
	var token string
	authHeader := r.Header.Get("Authorization")
	if authHeader != "" && strings.HasPrefix(authHeader, "Bearer ") {
		token = strings.TrimPrefix(authHeader, "Bearer ")
	} else if cookie, err := r.Cookie("session_token"); err == nil {
		token = cookie.Value
	}

	if token == "" {
		return "", fmt.Errorf("no session token found")
	}

	// Validate session
	pubkey, err := aa.nostrAuth.ValidateSession(token)
	if err != nil {
		return "", fmt.Errorf("invalid session: %w", err)
	}

	// Check if user is admin
	if !aa.IsAdmin(pubkey) {
		return "", fmt.Errorf("access denied: user is not an admin")
	}

	return pubkey, nil
}

// LogAdminAction logs an admin action to the database
func (aa *AdminAuth) LogAdminAction(adminPubkey, actionType, targetID, reason string) error {
	_, err := aa.db.Exec(`
		INSERT INTO admin_actions (admin_pubkey, action_type, target_id, reason, timestamp)
		VALUES (?, ?, ?, ?, ?)
	`, adminPubkey, actionType, targetID, reason, time.Now())

	if err != nil {
		return fmt.Errorf("failed to log admin action: %w", err)
	}

	return nil
}

// GetAdminActions retrieves admin actions with optional filtering
func (aa *AdminAuth) GetAdminActions(limit int, offset int, adminPubkey string) ([]AdminAction, error) {
	query := `
		SELECT id, admin_pubkey, action_type, target_id, reason, timestamp
		FROM admin_actions
	`
	args := []interface{}{}

	if adminPubkey != "" {
		query += " WHERE admin_pubkey = ?"
		args = append(args, adminPubkey)
	}

	query += " ORDER BY timestamp DESC LIMIT ? OFFSET ?"
	args = append(args, limit, offset)

	rows, err := aa.db.Query(query, args...)
	if err != nil {
		return nil, fmt.Errorf("failed to query admin actions: %w", err)
	}
	defer rows.Close()

	var actions []AdminAction
	for rows.Next() {
		var action AdminAction
		err := rows.Scan(&action.ID, &action.AdminPubkey, &action.ActionType, 
			&action.TargetID, &action.Reason, &action.Timestamp)
		if err != nil {
			return nil, fmt.Errorf("failed to scan admin action: %w", err)
		}
		actions = append(actions, action)
	}

	return actions, nil
}

// AdminAction represents an admin action log entry
type AdminAction struct {
	ID          int       `json:"id"`
	AdminPubkey string    `json:"admin_pubkey"`
	ActionType  string    `json:"action_type"`
	TargetID    string    `json:"target_id"`
	Reason      string    `json:"reason"`
	Timestamp   time.Time `json:"timestamp"`
}

// BannedUser represents a banned user
type BannedUser struct {
	Pubkey    string    `json:"pubkey"`
	BannedBy  string    `json:"banned_by"`
	Reason    string    `json:"reason"`
	BannedAt  time.Time `json:"banned_at"`
}

// ContentReport represents a content report
type ContentReport struct {
	ID             int       `json:"id"`
	FileHash       string    `json:"file_hash"`
	ReporterPubkey string    `json:"reporter_pubkey"`
	Reason         string    `json:"reason"`
	Status         string    `json:"status"`
	CreatedAt      time.Time `json:"created_at"`
}

// BanUser bans a user with the given reason
func (aa *AdminAuth) BanUser(userPubkey, adminPubkey, reason string) error {
	// Check if user is already banned
	var exists bool
	err := aa.db.QueryRow("SELECT EXISTS(SELECT 1 FROM banned_users WHERE pubkey = ?)", userPubkey).Scan(&exists)
	if err != nil {
		return fmt.Errorf("failed to check ban status: %w", err)
	}

	if exists {
		return fmt.Errorf("user is already banned")
	}

	// Insert ban record
	_, err = aa.db.Exec(`
		INSERT INTO banned_users (pubkey, banned_by, reason, banned_at)
		VALUES (?, ?, ?, ?)
	`, userPubkey, adminPubkey, reason, time.Now())

	if err != nil {
		return fmt.Errorf("failed to ban user: %w", err)
	}

	// Log admin action
	return aa.LogAdminAction(adminPubkey, "ban_user", userPubkey, reason)
}

// UnbanUser removes a user ban
func (aa *AdminAuth) UnbanUser(userPubkey, adminPubkey, reason string) error {
	result, err := aa.db.Exec("DELETE FROM banned_users WHERE pubkey = ?", userPubkey)
	if err != nil {
		return fmt.Errorf("failed to unban user: %w", err)
	}

	rowsAffected, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("failed to check unban result: %w", err)
	}

	if rowsAffected == 0 {
		return fmt.Errorf("user is not banned")
	}

	// Log admin action
	return aa.LogAdminAction(adminPubkey, "unban_user", userPubkey, reason)
}

// IsUserBanned checks if a user is banned
func (aa *AdminAuth) IsUserBanned(pubkey string) (bool, error) {
	var exists bool
	err := aa.db.QueryRow("SELECT EXISTS(SELECT 1 FROM banned_users WHERE pubkey = ?)", pubkey).Scan(&exists)
	if err != nil {
		return false, fmt.Errorf("failed to check ban status: %w", err)
	}
	return exists, nil
}

// GetBannedUsers returns list of banned users
func (aa *AdminAuth) GetBannedUsers() ([]BannedUser, error) {
	rows, err := aa.db.Query(`
		SELECT pubkey, banned_by, reason, banned_at
		FROM banned_users
		ORDER BY banned_at DESC
	`)
	if err != nil {
		return nil, fmt.Errorf("failed to query banned users: %w", err)
	}
	defer rows.Close()

	var bannedUsers []BannedUser
	for rows.Next() {
		var user BannedUser
		err := rows.Scan(&user.Pubkey, &user.BannedBy, &user.Reason, &user.BannedAt)
		if err != nil {
			return nil, fmt.Errorf("failed to scan banned user: %w", err)
		}
		bannedUsers = append(bannedUsers, user)
	}

	return bannedUsers, nil
}