package queries

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

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

// MetricsQueries handles database operations for metrics
type MetricsQueries struct {
	db *sql.DB
}

func NewMetricsQueries(db *sql.DB) *MetricsQueries {
	return &MetricsQueries{db: db}
}

// CreateMetricsEventsBatch creates multiple metric events in a single transaction
func (q *MetricsQueries) CreateMetricsEventsBatch(events []models.StoredMetric) 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 metrics (
			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] metric_event_insert_failed event_id=%s error=%v", event.ID, err)
			continue
		}
	}

	return tx.Commit()
}

// GetMetrics retrieves metrics based on filter criteria
func (q *MetricsQueries) GetMetrics(filter *models.MetricFilter) (*models.MetricResult, error) {
	query := `
		SELECT id, agent_id, package_type, package_name, current_version, available_version,
		       severity, repository_source, metadata, event_type, created_at
		FROM metrics
		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.PackageType != nil {
		query += fmt.Sprintf(" AND package_type = $%d", argIndex)
		args = append(args, *filter.PackageType)
		argIndex++
	}

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

	// 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 metrics: %w", err)
	}
	defer rows.Close()

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

	// Get total count
	countQuery := `SELECT COUNT(*) FROM metrics 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.PackageType != nil {
		countQuery += fmt.Sprintf(" AND package_type = $%d", countIndex)
		countArgs = append(countArgs, *filter.PackageType)
		countIndex++
	}

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

	var total int
	err = q.db.QueryRow(countQuery, countArgs...).Scan(&total)
	if err != nil {
		return nil, fmt.Errorf("failed to count metrics: %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.MetricResult{
		Metrics: metrics,
		Total:   total,
		Page:    page,
		PerPage: perPage,
	}, nil
}

// GetMetricsByAgentID retrieves metrics for a specific agent
func (q *MetricsQueries) GetMetricsByAgentID(agentID uuid.UUID, limit int) ([]models.StoredMetric, error) {
	query := `
		SELECT id, agent_id, package_type, package_name, current_version, available_version,
		       severity, repository_source, metadata, event_type, created_at
		FROM metrics
		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 metrics by agent: %w", err)
	}
	defer rows.Close()

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

	return metrics, nil
}

// GetLatestMetricsByType retrieves the latest metrics for a specific type
func (q *MetricsQueries) GetLatestMetricsByType(agentID uuid.UUID, packageType string) (*models.StoredMetric, error) {
	query := `
		SELECT id, agent_id, package_type, package_name, current_version, available_version,
		       severity, repository_source, metadata, event_type, created_at
		FROM metrics
		WHERE agent_id = $1 AND package_type = $2
		ORDER BY created_at DESC
		LIMIT 1
	`

	var metric models.StoredMetric
	err := q.db.QueryRow(query, agentID, packageType).Scan(
		&metric.ID,
		&metric.AgentID,
		&metric.PackageType,
		&metric.PackageName,
		&metric.CurrentVersion,
		&metric.AvailableVersion,
		&metric.Severity,
		&metric.RepositorySource,
		&metric.Metadata,
		&metric.EventType,
		&metric.CreatedAt,
	)

	if err == sql.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, fmt.Errorf("failed to get latest metric: %w", err)
	}

	return &metric, nil
}

// DeleteOldMetrics deletes metrics older than the specified number of days
func (q *MetricsQueries) DeleteOldMetrics(days int) error {
	query := `DELETE FROM metrics 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 metrics: %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] metrics_cleanup removed=%d", rowsAffected)
	}

	return nil
}