package sqlite

import (
	"context"
	"fmt"
	"strings"
)

type ImportRun struct {
	RunID                 string
	HostID                string
	Mode                  string
	AccessMode            string
	SubscriptionUsersJSON string
	SubscriptionDays      int
	ProbeAPIKey           string
	State                 string
	TotalItems            int
	CompletedItems        int
	ActiveItems           int
	DegradedItems         int
	BrokenItems           int
	WarningItems          int
	StartedAt             string
	UpdatedAt             string
	FinishedAt            string
}

type ImportRunsRepo struct {
	db execQuerier
}

func newImportRunsRepo(db execQuerier) *ImportRunsRepo {
	return &ImportRunsRepo{db: db}
}

func (r *ImportRunsRepo) Create(ctx context.Context, run ImportRun) error {
	runID := strings.TrimSpace(run.RunID)
	hostID := strings.TrimSpace(run.HostID)
	mode := strings.TrimSpace(run.Mode)
	accessMode := strings.TrimSpace(run.AccessMode)
	subscriptionUsersJSON := defaultJSON(run.SubscriptionUsersJSON, "[]")
	probeAPIKey := strings.TrimSpace(run.ProbeAPIKey)
	state := strings.TrimSpace(run.State)

	switch {
	case runID == "":
		return fmt.Errorf("run_id is required")
	case hostID == "":
		return fmt.Errorf("host_id is required")
	case mode == "":
		return fmt.Errorf("mode is required")
	case accessMode == "":
		return fmt.Errorf("access_mode is required")
	case state == "":
		return fmt.Errorf("state is required")
	}

	if _, err := r.db.ExecContext(ctx, `INSERT INTO import_runs (run_id, host_id, mode, access_mode, subscription_users_json, subscription_days, probe_api_key, state, total_items, completed_items, active_items, degraded_items, broken_items, warning_items) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
		runID, hostID, mode, accessMode, subscriptionUsersJSON, run.SubscriptionDays, probeAPIKey, state, run.TotalItems, run.CompletedItems, run.ActiveItems, run.DegradedItems, run.BrokenItems, run.WarningItems); err != nil {
		return fmt.Errorf("insert import run %q: %w", runID, err)
	}
	return nil
}

func (r *ImportRunsRepo) Update(ctx context.Context, run ImportRun) error {
	runID := strings.TrimSpace(run.RunID)
	hostID := strings.TrimSpace(run.HostID)
	mode := strings.TrimSpace(run.Mode)
	accessMode := strings.TrimSpace(run.AccessMode)
	subscriptionUsersJSON := defaultJSON(run.SubscriptionUsersJSON, "[]")
	probeAPIKey := strings.TrimSpace(run.ProbeAPIKey)
	state := strings.TrimSpace(run.State)
	finishedAt := strings.TrimSpace(run.FinishedAt)

	switch {
	case runID == "":
		return fmt.Errorf("run_id is required")
	case hostID == "":
		return fmt.Errorf("host_id is required")
	case mode == "":
		return fmt.Errorf("mode is required")
	case accessMode == "":
		return fmt.Errorf("access_mode is required")
	case state == "":
		return fmt.Errorf("state is required")
	}

	if _, err := r.db.ExecContext(ctx, `UPDATE import_runs
			SET host_id = ?, mode = ?, access_mode = ?, subscription_users_json = ?, subscription_days = ?, probe_api_key = ?, state = ?, total_items = ?, completed_items = ?, active_items = ?, degraded_items = ?, broken_items = ?, warning_items = ?, finished_at = ?, updated_at = CURRENT_TIMESTAMP
			WHERE run_id = ?`,
		hostID, mode, accessMode, subscriptionUsersJSON, run.SubscriptionDays, probeAPIKey, state, run.TotalItems, run.CompletedItems, run.ActiveItems, run.DegradedItems, run.BrokenItems, run.WarningItems, nullableString(finishedAt), runID); err != nil {
		return fmt.Errorf("update import run %q: %w", runID, err)
	}
	return nil
}

func (r *ImportRunsRepo) GetByRunID(ctx context.Context, runID string) (ImportRun, error) {
	runID = strings.TrimSpace(runID)
	if runID == "" {
		return ImportRun{}, fmt.Errorf("run_id is required")
	}

	var run ImportRun
	if err := r.db.QueryRowContext(ctx, `SELECT run_id, host_id, mode, access_mode, subscription_users_json, subscription_days, COALESCE(probe_api_key, ''), state, total_items, completed_items, active_items, degraded_items, broken_items, warning_items, started_at, updated_at, COALESCE(finished_at, '') FROM import_runs WHERE run_id = ?`, runID).
		Scan(&run.RunID, &run.HostID, &run.Mode, &run.AccessMode, &run.SubscriptionUsersJSON, &run.SubscriptionDays, &run.ProbeAPIKey, &run.State, &run.TotalItems, &run.CompletedItems, &run.ActiveItems, &run.DegradedItems, &run.BrokenItems, &run.WarningItems, &run.StartedAt, &run.UpdatedAt, &run.FinishedAt); err != nil {
		return ImportRun{}, err
	}
	return run, nil
}

func (r *ImportRunsRepo) List(ctx context.Context, limit int) ([]ImportRun, error) {
	if limit <= 0 {
		limit = 50
	}

	rows, err := r.db.QueryContext(ctx, `SELECT run_id, host_id, mode, access_mode, subscription_users_json, subscription_days, COALESCE(probe_api_key, ''), state, total_items, completed_items, active_items, degraded_items, broken_items, warning_items, started_at, updated_at, COALESCE(finished_at, '') FROM import_runs ORDER BY started_at DESC LIMIT ?`, limit)
	if err != nil {
		return nil, fmt.Errorf("list import runs: %w", err)
	}
	defer rows.Close()

	runs := make([]ImportRun, 0)
	for rows.Next() {
		var run ImportRun
		if err := rows.Scan(&run.RunID, &run.HostID, &run.Mode, &run.AccessMode, &run.SubscriptionUsersJSON, &run.SubscriptionDays, &run.ProbeAPIKey, &run.State, &run.TotalItems, &run.CompletedItems, &run.ActiveItems, &run.DegradedItems, &run.BrokenItems, &run.WarningItems, &run.StartedAt, &run.UpdatedAt, &run.FinishedAt); err != nil {
			return nil, fmt.Errorf("scan import run: %w", err)
		}
		runs = append(runs, run)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate import runs: %w", err)
	}
	return runs, nil
}

type sqlNullInt64 struct {
	Int64 int64
	Valid bool
}

func (n sqlNullInt64) ptr() *int64 {
	if !n.Valid {
		return nil
	}
	value := n.Int64
	return &value
}

func nullableString(value string) any {
	if strings.TrimSpace(value) == "" {
		return nil
	}
	return value
}

func defaultJSON(value, fallback string) string {
	value = strings.TrimSpace(value)
	if value == "" {
		return fallback
	}
	return value
}
