package {{.PackageName}}

import (
	"database/sql"
	"iter"
)

type TX interface {
	Exec(query string, args ...any) (sql.Result, error)
	Query(query string, args ...any) (*sql.Rows, error)
	QueryRow(query string, args ...any) *sql.Row
}

{{range .Schema.Tables}}

// ----------------------------------------------------------------------------
// Table: {{.Name}}
// ----------------------------------------------------------------------------

type {{.Type}} struct {
	{{- range .Columns}}
	{{.Name}} {{.Type}}{{end}}
}

const {{.Type}}_SelectQuery = "{{.SelectQuery}}"

{{if not .NoInsert -}}

func {{.Type}}_Insert(
	tx TX,
	row *{{.Type}},
) (err error) {
  {{.Type}}_Sanitize(row)
  if err = {{.Type}}_Validate(row); err != nil {
    return err
  }

	_, err = tx.Exec("{{.InsertQuery}}", {{.InsertArgs}})
	return err
}

{{- end}} {{/* if not .NoInsert */}}

{{if not .NoUpdate -}}

{{if .UpdateCols -}}

func {{.Type}}_Update(
	tx TX,
	row *{{.Type}},
) (err error) {
  {{.Type}}_Sanitize(row)
  if err = {{.Type}}_Validate(row); err != nil {
    return err
  }

	result, err := tx.Exec("{{.UpdateQuery}}", {{.UpdateArgs}})
	if err != nil {
		return err
	}

	n, err := result.RowsAffected()
	if err != nil {
		panic(err)
	}
	switch n {
	case 0:
		return sql.ErrNoRows
	case 1:
		return nil
	default:
		panic("multiple rows updated")
	}
}
{{- end}}

{{if .UpdateFullCols -}}

func {{.Type}}_UpdateFull(
	tx TX,
	row *{{.Type}},
) (err error) {
  {{.Type}}_Sanitize(row)
  if err = {{.Type}}_Validate(row); err != nil {
    return err
  }

	result, err := tx.Exec("{{.UpdateFullQuery}}", {{.UpdateFullArgs}})
	if err != nil {
		return err
	}

	n, err := result.RowsAffected()
	if err != nil {
		panic(err)
	}
	switch n {
		case 0:
		return sql.ErrNoRows
	case 1:
		return nil
	default:
		panic("multiple rows updated")
	}
}
{{- end}}


{{- end}} {{/* if not .NoUpdate */}}

{{if not .NoDelete -}}

func {{.Type}}_Delete(
	tx TX,
	{{.PKFunctionArgs -}}
) (err error) {
	result, err := tx.Exec("{{.DeleteQuery}}", {{.DeleteArgs}})
	if err != nil {
		return err
	}

	n, err := result.RowsAffected()
	if err != nil {
		panic(err)
	}
	switch n {
		case 0:
		return sql.ErrNoRows
	case 1:
		return nil
	default:
		panic("multiple rows deleted")
	}
}

{{- end}}

func {{.Type}}_Get(
	tx TX,
	{{.PKFunctionArgs -}}
) (
	row *{{.Type}},
	err error,
) {
  row = &{{.Type}}{}
	r := tx.QueryRow("{{.GetQuery}}", {{.DeleteArgs}})
	err = r.Scan({{.ScanArgs}})
	return
}


func {{.Type}}_GetWhere(
	tx TX,
	query string,
	args ...any,
) (
	row *{{.Type}},
	err error,
) {
  row = &{{.Type}}{}
	r := tx.QueryRow(query, args...)
	err = r.Scan({{.ScanArgs}})
	return
}



func {{.Type}}_Iterate(
	tx TX,
	query string,
	args ...any,
) (
	iter.Seq2[*{{.Type}}, error],
) {
	rows, err := tx.Query(query, args...)
	if err != nil {
		return func(yield func(*{{.Type}}, error) bool) {
			yield(nil, err)
		}
	}

	return func(yield func(*{{.Type}}, error) bool) {
		defer rows.Close()
		for rows.Next() {
			row := &{{.Type}}{}
			err := rows.Scan({{.ScanArgs}})
			if !yield(row, err) {
				return
			}
		}
	}
}

func {{.Type}}_List(
	tx TX,
	query string,
	args ...any,
) (
	l []*{{.Type}},
	err error,
) {
	for row, err := range {{.Type}}_Iterate(tx, query, args...) {
		if err != nil {
			return nil, err
		}
		l = append(l, row)
	}
	return l, nil
}


{{end}} {{/* range .Schema.Tables */}}