package db

// MillLease is the persisted view of a mill-side remote lease, enough to
// rebuild the fencing token and its workflow identity after a mill restart.
type MillLease struct {
	LeaseID  string
	NodeID   string
	Engine   string
	Knot     string
	Rkey     string
	Workflow string
	State    string
}

func (d *DB) SaveMillLease(l MillLease) error {
	_, err := d.Exec(
		`insert into mill_leases (lease_id, node_id, engine, knot, rkey, workflow, state)
		 values (?, ?, ?, ?, ?, ?, ?)
		 on conflict(lease_id) do update set state = excluded.state`,
		l.LeaseID, l.NodeID, l.Engine, l.Knot, l.Rkey, l.Workflow, l.State,
	)
	return err
}

func (d *DB) DeleteMillLease(leaseID string) error {
	_, err := d.Exec(`delete from mill_leases where lease_id = ?`, leaseID)
	return err
}

func (d *DB) DeleteMillLeasesByNode(nodeID string) error {
	_, err := d.Exec(`delete from mill_leases where node_id = ?`, nodeID)
	return err
}

func (d *DB) ListMillLeases() ([]MillLease, error) {
	rows, err := d.Query(`select lease_id, node_id, engine, knot, rkey, workflow, state from mill_leases`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var leases []MillLease
	for rows.Next() {
		var l MillLease
		if err := rows.Scan(&l.LeaseID, &l.NodeID, &l.Engine, &l.Knot, &l.Rkey, &l.Workflow, &l.State); err != nil {
			return nil, err
		}
		leases = append(leases, l)
	}
	return leases, rows.Err()
}

// SetExecutorCursor persists the highest relay offset the mill has applied for
// a node.
func (d *DB) SetExecutorCursor(nodeID string, offset uint64) error {
	_, err := d.Exec(
		`insert into mill_executor_cursors (node_id, acked_offset) values (?, ?)
		 on conflict(node_id) do update set acked_offset = excluded.acked_offset`,
		nodeID, offset,
	)
	return err
}

func (d *DB) DeleteExecutorCursor(nodeID string) error {
	_, err := d.Exec(`delete from mill_executor_cursors where node_id = ?`, nodeID)
	return err
}

func (d *DB) ListExecutorCursors() (map[string]uint64, error) {
	rows, err := d.Query(`select node_id, acked_offset from mill_executor_cursors`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	cursors := make(map[string]uint64)
	for rows.Next() {
		var node string
		var offset uint64
		if err := rows.Scan(&node, &offset); err != nil {
			return nil, err
		}
		cursors[node] = offset
	}
	return cursors, rows.Err()
}
