package sqlite

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"time"

	"github.com/google/uuid"

	"switchboard-core/database"
	"switchboard-core/models"
	"switchboard-core/store"
)

type ScheduledTaskStore struct{}

func NewScheduledTaskStore() *ScheduledTaskStore {
	return &ScheduledTaskStore{}
}

func (s *ScheduledTaskStore) Create(ctx context.Context, t *models.ScheduledTask) error {
	t.ID = uuid.New().String()
	now := time.Now().UTC().Format(time.RFC3339)
	t.CreatedAt, _ = time.Parse(time.RFC3339, now)
	t.UpdatedAt = t.CreatedAt

	params, _ := json.Marshal(t.TemplateParams)
	if len(params) == 0 {
		params = []byte("{}")
	}

	var nextFire *string
	if t.NextFireAt != nil {
		s := t.NextFireAt.UTC().Format(time.RFC3339)
		nextFire = &s
	}

	_, err := DB.ExecContext(ctx,
		`INSERT INTO scheduled_tasks (id, name, description, creator_id, run_as, cron_expr,
		 next_fire_at, enabled, script, template_id, template_params, created_at, updated_at)
		 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
		t.ID, t.Name, t.Description, t.CreatorID, t.RunAs, t.CronExpr,
		nextFire, boolToInt(t.Enabled), t.Script,
		nullIfEmpty(t.TemplateID), string(params), now, now)
	return err
}

func (s *ScheduledTaskStore) GetByID(ctx context.Context, id string) (*models.ScheduledTask, error) {
	var t models.ScheduledTask
	var paramsStr string
	err := DB.QueryRowContext(ctx,
		`SELECT id, name, description, creator_id, run_as, cron_expr,
		 next_fire_at, last_fire_at, enabled, script,
		 COALESCE(template_id,''), template_params, fire_count,
		 COALESCE(last_error,''), last_duration_ms, created_at, updated_at
		 FROM scheduled_tasks WHERE id = ?`, id).
		Scan(&t.ID, &t.Name, &t.Description, &t.CreatorID, &t.RunAs, &t.CronExpr,
			database.SNT(&t.NextFireAt), database.SNT(&t.LastFireAt),
			&t.Enabled, &t.Script,
			&t.TemplateID, &paramsStr, &t.FireCount,
			&t.LastError, &t.LastDurationMs,
			database.ST(&t.CreatedAt), database.ST(&t.UpdatedAt))
	if err == sql.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	t.TemplateParams = json.RawMessage(paramsStr)
	return &t, nil
}

func (s *ScheduledTaskStore) Update(ctx context.Context, t *models.ScheduledTask) error {
	params, _ := json.Marshal(t.TemplateParams)
	if len(params) == 0 {
		params = []byte("{}")
	}
	var nextFire *string
	if t.NextFireAt != nil {
		s := t.NextFireAt.UTC().Format(time.RFC3339)
		nextFire = &s
	}
	_, err := DB.ExecContext(ctx,
		`UPDATE scheduled_tasks SET name = ?, description = ?, run_as = ?,
		 cron_expr = ?, next_fire_at = ?, enabled = ?, script = ?,
		 template_id = ?, template_params = ?, updated_at = datetime('now')
		 WHERE id = ?`,
		t.Name, t.Description, t.RunAs, t.CronExpr, nextFire,
		boolToInt(t.Enabled), t.Script,
		nullIfEmpty(t.TemplateID), string(params), t.ID)
	return err
}

func (s *ScheduledTaskStore) Delete(ctx context.Context, id string) error {
	_, err := DB.ExecContext(ctx, `DELETE FROM scheduled_tasks WHERE id = ?`, id)
	return err
}

func (s *ScheduledTaskStore) List(ctx context.Context, opts store.ScheduledTaskListOptions) ([]models.ScheduledTask, int, error) {
	where := "1=1"
	args := []any{}

	if opts.CreatorID != "" {
		where += " AND creator_id = ?"
		args = append(args, opts.CreatorID)
	}
	if opts.Enabled != nil {
		where += " AND enabled = ?"
		args = append(args, boolToInt(*opts.Enabled))
	}

	var total int
	countArgs := make([]any, len(args))
	copy(countArgs, args)
	err := DB.QueryRowContext(ctx, "SELECT COUNT(*) FROM scheduled_tasks WHERE "+where, countArgs...).Scan(&total)
	if err != nil {
		return nil, 0, err
	}

	limit := opts.Limit
	if limit <= 0 {
		limit = 50
	}
	query := fmt.Sprintf(
		`SELECT id, name, description, creator_id, run_as, cron_expr,
		 next_fire_at, last_fire_at, enabled, script,
		 COALESCE(template_id,''), template_params, fire_count,
		 COALESCE(last_error,''), last_duration_ms, created_at, updated_at
		 FROM scheduled_tasks WHERE %s ORDER BY created_at DESC LIMIT ? OFFSET ?`, where)
	args = append(args, limit, opts.Offset)

	rows, err := DB.QueryContext(ctx, query, args...)
	if err != nil {
		return nil, 0, err
	}
	defer rows.Close()

	tasks, err := scanScheduledTaskRowsSqlite(rows)
	return tasks, total, err
}

func (s *ScheduledTaskStore) ListByCreator(ctx context.Context, creatorID string) ([]models.ScheduledTask, error) {
	rows, err := DB.QueryContext(ctx,
		`SELECT id, name, description, creator_id, run_as, cron_expr,
		 next_fire_at, last_fire_at, enabled, script,
		 COALESCE(template_id,''), template_params, fire_count,
		 COALESCE(last_error,''), last_duration_ms, created_at, updated_at
		 FROM scheduled_tasks WHERE creator_id = ? ORDER BY created_at DESC`, creatorID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	return scanScheduledTaskRowsSqlite(rows)
}

func (s *ScheduledTaskStore) ListEnabled(ctx context.Context) ([]models.ScheduledTask, error) {
	rows, err := DB.QueryContext(ctx,
		`SELECT id, name, description, creator_id, run_as, cron_expr,
		 next_fire_at, last_fire_at, enabled, script,
		 COALESCE(template_id,''), template_params, fire_count,
		 COALESCE(last_error,''), last_duration_ms, created_at, updated_at
		 FROM scheduled_tasks WHERE enabled = 1 ORDER BY created_at`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	return scanScheduledTaskRowsSqlite(rows)
}

func (s *ScheduledTaskStore) SetEnabled(ctx context.Context, id string, enabled bool) error {
	_, err := DB.ExecContext(ctx,
		`UPDATE scheduled_tasks SET enabled = ?, updated_at = datetime('now') WHERE id = ?`,
		boolToInt(enabled), id)
	return err
}

func (s *ScheduledTaskStore) UpdateFireState(ctx context.Context, id string, lastFireAt time.Time, nextFireAt *time.Time, lastError string, durationMs int) error {
	var nextFire *string
	if nextFireAt != nil {
		s := nextFireAt.UTC().Format(time.RFC3339)
		nextFire = &s
	}
	_, err := DB.ExecContext(ctx,
		`UPDATE scheduled_tasks SET last_fire_at = ?, next_fire_at = ?,
		 last_error = ?, last_duration_ms = ?, fire_count = fire_count + 1,
		 updated_at = datetime('now') WHERE id = ?`,
		lastFireAt.UTC().Format(time.RFC3339), nextFire,
		nullIfEmpty(lastError), durationMs, id)
	return err
}

// ── Helpers ──────────────────────────────────

func scanScheduledTaskRowsSqlite(rows *sql.Rows) ([]models.ScheduledTask, error) {
	var tasks []models.ScheduledTask
	for rows.Next() {
		var t models.ScheduledTask
		var paramsStr string
		if err := rows.Scan(&t.ID, &t.Name, &t.Description, &t.CreatorID, &t.RunAs,
			&t.CronExpr, database.SNT(&t.NextFireAt), database.SNT(&t.LastFireAt),
			&t.Enabled, &t.Script,
			&t.TemplateID, &paramsStr, &t.FireCount,
			&t.LastError, &t.LastDurationMs,
			database.ST(&t.CreatedAt), database.ST(&t.UpdatedAt)); err != nil {
			return nil, err
		}
		t.TemplateParams = json.RawMessage(paramsStr)
		tasks = append(tasks, t)
	}
	if tasks == nil {
		tasks = []models.ScheduledTask{}
	}
	return tasks, nil
}
