package queries

import (
	"database/sql"
	"fmt"
	"log"

	"github.com/Fimeg/RedFlag/aggregator-server/internal/models"
	"github.com/google/uuid"
)

// DockerQueries handles database operations for Docker images
type DockerQueries struct {
	db *sql.DB
}

func NewDockerQueries(db *sql.DB) *DockerQueries {
	return &DockerQueries{db: db}
}

// CreateDockerEventsBatch creates multiple Docker image events in a single transaction
func (q *DockerQueries) CreateDockerEventsBatch(events []models.StoredDockerImage) error {
	if len(events) == 0 {
		return nil
	}

	tx, err := q.db.Begin()
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer tx.Rollback()

	// Prepare the insert statement
	stmt, err := tx.Prepare(`
		INSERT INTO docker_images (
			id, agent_id, package_type, package_name, current_version, available_version,
			severity, repository_source, metadata, event_type, created_at
		) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
		ON CONFLICT (agent_id, package_name, package_type, created_at) DO NOTHING
	`)
	if err != nil {
		return fmt.Errorf("failed to prepare statement: %w", err)
	}
	defer stmt.Close()

	// Insert each event with error isolation
	for _, event := range events {
		_, err := stmt.Exec(
			event.ID,
			event.AgentID,
			event.PackageType,
			event.PackageName,
			event.CurrentVersion,
			event.AvailableVersion,
			event.Severity,
			event.RepositorySource,
			event.Metadata,
			event.EventType,
			event.CreatedAt,
		)
		if err != nil {
			// Log error but continue with other events
			log.Printf("[WARNING] [server] [database] docker_event_insert_failed event_id=%s error=%v", event.ID, err)
			continue
		}
	}

	return tx.Commit()
}

// GetDockerImages retrieves Docker images based on filter criteria
func (q *DockerQueries) GetDockerImages(filter *models.DockerFilter) (*models.DockerResult, error) {
	query := `
		SELECT id, agent_id, package_type, package_name, current_version, available_version,
		       severity, repository_source, metadata, event_type, created_at
		FROM docker_images
		WHERE 1=1
	`
	args := []interface{}{}
	argIndex := 1

	// Build WHERE clause
	if filter.AgentID != nil {
		query += fmt.Sprintf(" AND agent_id = $%d", argIndex)
		args = append(args, *filter.AgentID)
		argIndex++
	}

	if filter.ImageName != nil {
		query += fmt.Sprintf(" AND package_name ILIKE $%d", argIndex)
		args = append(args, "%"+*filter.ImageName+"%")
		argIndex++
	}

	if filter.Registry != nil {
		query += fmt.Sprintf(" AND repository_source ILIKE $%d", argIndex)
		args = append(args, "%"+*filter.Registry+"%")
		argIndex++
	}

	if filter.Severity != nil {
		query += fmt.Sprintf(" AND severity = $%d", argIndex)
		args = append(args, *filter.Severity)
		argIndex++
	}

	if filter.HasUpdates != nil {
		if *filter.HasUpdates {
			query += " AND current_version != available_version"
		} else {
			query += " AND current_version = available_version"
		}
	}

	// Add ordering and pagination
	query += " ORDER BY created_at DESC"

	if filter.Limit != nil {
		query += fmt.Sprintf(" LIMIT $%d", argIndex)
		args = append(args, *filter.Limit)
		argIndex++
	}

	if filter.Offset != nil {
		query += fmt.Sprintf(" OFFSET $%d", argIndex)
		args = append(args, *filter.Offset)
		argIndex++
	}

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

	var images []models.StoredDockerImage
	for rows.Next() {
		var image models.StoredDockerImage
		err := rows.Scan(
			&image.ID,
			&image.AgentID,
			&image.PackageType,
			&image.PackageName,
			&image.CurrentVersion,
			&image.AvailableVersion,
			&image.Severity,
			&image.RepositorySource,
			&image.Metadata,
			&image.EventType,
			&image.CreatedAt,
		)
		if err != nil {
			return nil, fmt.Errorf("failed to scan docker image: %w", err)
		}
		images = append(images, image)
	}

	// Get total count
	countQuery := `SELECT COUNT(*) FROM docker_images WHERE 1=1`
	countArgs := []interface{}{}
	countIndex := 1

	if filter.AgentID != nil {
		countQuery += fmt.Sprintf(" AND agent_id = $%d", countIndex)
		countArgs = append(countArgs, *filter.AgentID)
		countIndex++
	}

	if filter.ImageName != nil {
		countQuery += fmt.Sprintf(" AND package_name ILIKE $%d", countIndex)
		countArgs = append(countArgs, "%"+*filter.ImageName+"%")
		countIndex++
	}

	if filter.Registry != nil {
		countQuery += fmt.Sprintf(" AND repository_source ILIKE $%d", countIndex)
		countArgs = append(countArgs, "%"+*filter.Registry+"%")
		countIndex++
	}

	if filter.Severity != nil {
		countQuery += fmt.Sprintf(" AND severity = $%d", countIndex)
		countArgs = append(countArgs, *filter.Severity)
		countIndex++
	}

	if filter.HasUpdates != nil {
		if *filter.HasUpdates {
			countQuery += " AND current_version != available_version"
		} else {
			countQuery += " AND current_version = available_version"
		}
	}

	var total int
	err = q.db.QueryRow(countQuery, countArgs...).Scan(&total)
	if err != nil {
		return nil, fmt.Errorf("failed to count docker images: %w", err)
	}

	// Calculate pagination
	page := 1
	perPage := 50
	if filter.Offset != nil && filter.Limit != nil {
		page = (*filter.Offset / *filter.Limit) + 1
		perPage = *filter.Limit
	}

	return &models.DockerResult{
		Images:  images,
		Total:   total,
		Page:    page,
		PerPage: perPage,
	}, nil
}

// GetDockerImagesByAgentID retrieves Docker images for a specific agent
func (q *DockerQueries) GetDockerImagesByAgentID(agentID uuid.UUID, limit int) ([]models.StoredDockerImage, error) {
	query := `
		SELECT id, agent_id, package_type, package_name, current_version, available_version,
		       severity, repository_source, metadata, event_type, created_at
		FROM docker_images
		WHERE agent_id = $1
		ORDER BY created_at DESC
		LIMIT $2
	`

	rows, err := q.db.Query(query, agentID, limit)
	if err != nil {
		return nil, fmt.Errorf("failed to query docker images by agent: %w", err)
	}
	defer rows.Close()

	var images []models.StoredDockerImage
	for rows.Next() {
		var image models.StoredDockerImage
		err := rows.Scan(
			&image.ID,
			&image.AgentID,
			&image.PackageType,
			&image.PackageName,
			&image.CurrentVersion,
			&image.AvailableVersion,
			&image.Severity,
			&image.RepositorySource,
			&image.Metadata,
			&image.EventType,
			&image.CreatedAt,
		)
		if err != nil {
			return nil, fmt.Errorf("failed to scan docker image: %w", err)
		}
		images = append(images, image)
	}

	return images, nil
}

// GetDockerImagesWithUpdates retrieves Docker images that have available updates
func (q *DockerQueries) GetDockerImagesWithUpdates(limit int) ([]models.StoredDockerImage, error) {
	query := `
		SELECT id, agent_id, package_type, package_name, current_version, available_version,
		       severity, repository_source, metadata, event_type, created_at
		FROM docker_images
		WHERE current_version != available_version
		ORDER BY created_at DESC
		LIMIT $1
	`

	rows, err := q.db.Query(query, limit)
	if err != nil {
		return nil, fmt.Errorf("failed to query docker images with updates: %w", err)
	}
	defer rows.Close()

	var images []models.StoredDockerImage
	for rows.Next() {
		var image models.StoredDockerImage
		err := rows.Scan(
			&image.ID,
			&image.AgentID,
			&image.PackageType,
			&image.PackageName,
			&image.CurrentVersion,
			&image.AvailableVersion,
			&image.Severity,
			&image.RepositorySource,
			&image.Metadata,
			&image.EventType,
			&image.CreatedAt,
		)
		if err != nil {
			return nil, fmt.Errorf("failed to scan docker image: %w", err)
		}
		images = append(images, image)
	}

	return images, nil
}

// DeleteOldDockerImages deletes Docker images older than the specified number of days
func (q *DockerQueries) DeleteOldDockerImages(days int) error {
	query := `DELETE FROM docker_images WHERE created_at < NOW() - INTERVAL '1 day' * $1`

	result, err := q.db.Exec(query, days)
	if err != nil {
		return fmt.Errorf("failed to delete old docker images: %w", err)
	}

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

	if rowsAffected > 0 {
		log.Printf("[INFO] [server] [database] docker_cleanup removed=%d", rowsAffected)
	}

	return nil
}

// GetDockerStats returns statistics about Docker images across all agents
func (q *DockerQueries) GetDockerStats() (*models.DockerStats, error) {
	var stats models.DockerStats

	// Get total images
	err := q.db.QueryRow("SELECT COUNT(*) FROM docker_images").Scan(&stats.TotalImages)
	if err != nil {
		return nil, fmt.Errorf("failed to get total docker images: %w", err)
	}

	// Get images with updates
	err = q.db.QueryRow("SELECT COUNT(*) FROM docker_images WHERE current_version != available_version").Scan(&stats.UpdatesAvailable)
	if err != nil {
		return nil, fmt.Errorf("failed to get docker images with updates: %w", err)
	}

	// Get critical updates
	err = q.db.QueryRow("SELECT COUNT(*) FROM docker_images WHERE severity = 'critical' AND current_version != available_version").Scan(&stats.CriticalUpdates)
	if err != nil {
		return nil, fmt.Errorf("failed to get critical docker updates: %w", err)
	}

	// Get agents with Docker images
	err = q.db.QueryRow("SELECT COUNT(DISTINCT agent_id) FROM docker_images").Scan(&stats.AgentsWithContainers)
	if err != nil {
		return nil, fmt.Errorf("failed to get agents with docker images: %w", err)
	}

	return &stats, nil
}