package db

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

	"github.com/jackc/pgx/v5"
)

// ScheduleRow is the persisted form of a schedule.
type ScheduleRow struct {
	ID                    string
	InstanceID            string
	Description           string
	TriggerKind           string // "cron" | "event"
	CronSpec              string // populated for TriggerKind == "cron"
	EventSpec             string // populated for TriggerKind == "event" — CEL expression returning bool
	EventSustainedSeconds int32  // for event: fire only after condition true this many seconds
	Action                json.RawMessage
	Enabled               bool
	LastFiredAt           *time.Time
	LastStatus            string
	LastDetail            string
	CreatedAt             time.Time
	UpdatedAt             time.Time
}

func (db *DB) InsertSchedule(ctx context.Context, r ScheduleRow) error {
	var cronSpec, eventSpec *string
	if r.CronSpec != "" {
		cronSpec = &r.CronSpec
	}
	if r.EventSpec != "" {
		eventSpec = &r.EventSpec
	}
	_, err := db.pool.Exec(ctx, `
		INSERT INTO schedules (id, instance_id, description, trigger_kind, cron_spec, event_spec, event_sustained_seconds, action, enabled)
		VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
	`, r.ID, r.InstanceID, r.Description, r.TriggerKind, cronSpec, eventSpec, r.EventSustainedSeconds, []byte(r.Action), r.Enabled)
	if err != nil {
		return fmt.Errorf("insert schedule: %w", err)
	}
	return nil
}

func (db *DB) DeleteSchedule(ctx context.Context, id string) error {
	_, err := db.pool.Exec(ctx, `DELETE FROM schedules WHERE id = $1`, id)
	if err != nil {
		return fmt.Errorf("delete schedule: %w", err)
	}
	return nil
}

func (db *DB) SetScheduleEnabled(ctx context.Context, id string, enabled bool) error {
	_, err := db.pool.Exec(ctx, `UPDATE schedules SET enabled = $2, updated_at = NOW() WHERE id = $1`, id, enabled)
	if err != nil {
		return fmt.Errorf("set schedule enabled: %w", err)
	}
	return nil
}

// UpdateSchedule persists changes to an existing schedule row.
func (db *DB) UpdateSchedule(ctx context.Context, r ScheduleRow) error {
	_, err := db.pool.Exec(ctx, `
		UPDATE schedules
		   SET description = $2,
		       trigger_kind = $3,
		       cron_spec = $4,
		       event_spec = $5,
		       event_sustained_seconds = $6,
		       action = $7,
		       enabled = $8,
		       updated_at = NOW()
		 WHERE id = $1`,
		r.ID, r.Description, r.TriggerKind, r.CronSpec, r.EventSpec, r.EventSustainedSeconds, []byte(r.Action), r.Enabled)
	if err != nil {
		return fmt.Errorf("update schedule: %w", err)
	}
	return nil
}

// UpdateScheduleFireResult records the outcome of a trigger fire — used by
// the engine to surface success/failure in the UI.
func (db *DB) UpdateScheduleFireResult(ctx context.Context, id, status, detail string) error {
	_, err := db.pool.Exec(ctx, `
		UPDATE schedules
		SET last_fired_at = NOW(), last_status = $2, last_detail = $3, updated_at = NOW()
		WHERE id = $1
	`, id, status, detail)
	if err != nil {
		return fmt.Errorf("update schedule result: %w", err)
	}
	return nil
}

// ListSchedules returns every schedule, optionally filtered by instance_id.
func (db *DB) ListSchedules(ctx context.Context, instanceFilter string) ([]ScheduleRow, error) {
	const selectAll = `
		SELECT id, instance_id, description, trigger_kind,
		       COALESCE(cron_spec, ''), COALESCE(event_spec, ''), event_sustained_seconds,
		       action,
		       enabled, last_fired_at, COALESCE(last_status, ''), COALESCE(last_detail, ''),
		       created_at, updated_at
		FROM schedules
	`
	var rows pgx.Rows
	var err error
	if instanceFilter != "" {
		rows, err = db.pool.Query(ctx, selectAll+` WHERE instance_id = $1 ORDER BY id`, instanceFilter)
	} else {
		rows, err = db.pool.Query(ctx, selectAll+` ORDER BY instance_id, id`)
	}
	if err != nil {
		return nil, fmt.Errorf("list schedules: %w", err)
	}
	defer rows.Close()

	var out []ScheduleRow
	for rows.Next() {
		var r ScheduleRow
		var action []byte
		var lastFired *time.Time
		if err := rows.Scan(
			&r.ID, &r.InstanceID, &r.Description, &r.TriggerKind,
			&r.CronSpec, &r.EventSpec, &r.EventSustainedSeconds,
			&action,
			&r.Enabled, &lastFired, &r.LastStatus, &r.LastDetail,
			&r.CreatedAt, &r.UpdatedAt,
		); err != nil {
			return nil, fmt.Errorf("scan schedule: %w", err)
		}
		r.Action = action
		r.LastFiredAt = lastFired
		out = append(out, r)
	}
	return out, rows.Err()
}
