package gen

import (
	"os"
	"text/template"

	"github.com/Masterminds/sprig/v3"
)

func genFetch(gd *genData) {
	data := genDMLGenericData(gd)

	f, err := os.Create("fetch.go")
	if err != nil {
		panic(err)
	}

	defer f.Close()

	if err := fetchTemplate.Execute(f, data); err != nil {
		panic(err)
	}
}

var fetchTemplate = template.Must(template.New("").Funcs(sprig.TxtFuncMap()).Funcs(funcs).Parse(`
// Code generated by go generate; DO NOT EDIT.
package db

import (
	stdsql "database/sql"

	{{- if .HasJSON }}
	"encoding/json"
	{{- end }}

	"github.com/sorintlab/errors"
	sq "github.com/huandu/go-sqlbuilder"

	"agola.io/agola/internal/sqlg"
	"agola.io/agola/internal/sqlg/sql"

	types "{{ .TypesImport -}}"

	{{range $import := .AdditionalImports -}}
	"{{ $import }}"
	{{- end}}
)

{{- range $tableDef := .TableDefs }}

func (d *DB) fetch{{ $tableDef.ObjectName }}s(tx *sql.Tx, q sq.Builder) ([]*types.{{ $tableDef.ObjectName }}, []string, error) {
	rows, err := d.query(tx, q)
	if err != nil {
		return nil, nil, errors.WithStack(err)
	}
	defer rows.Close()

	return d.scan{{ $tableDef.ObjectName }}s(rows, tx.ID(), 0)
}

func (d *DB) fetch{{ $tableDef.ObjectName }}sSkipLastFields(tx *sql.Tx, q sq.Builder, skipFieldsCount uint) ([]*types.{{ $tableDef.ObjectName }}, []string, error) {
	rows, err := d.query(tx, q)
	if err != nil {
		return nil, nil, errors.WithStack(err)
	}
	defer rows.Close()

	return d.scan{{ $tableDef.ObjectName }}s(rows, tx.ID(), skipFieldsCount)
}

func (d *DB) scan{{ $tableDef.ObjectName }}(rows *stdsql.Rows, skipFieldsCount uint) (*types.{{ $tableDef.ObjectName }}, string, error) {
	{{- range $j := $tableDef.JSONValues }}
	var {{ $j.VarName }}JSON []byte
	{{- end }}

	v := &types.{{ $tableDef.ObjectName }}{}

	var vi any = v
	if x, ok := vi.(sqlg.Initer); ok {
		x.Init()
	}

	fields := []any{ {{- $tableDef.ScanFields | join ", " -}} }

	for i := uint(0); i < skipFieldsCount; i++ {
		fields = append(fields, new(any))
	}

	if err := rows.Scan(fields...); err != nil {
		return nil, "", errors.Wrap(err, "failed to scan row")
	}

	if x, ok := vi.(sqlg.PreJSONSetupper); ok {
		if err := x.PreJSON(); err != nil {
			return nil, "", errors.Wrap(err, "prejson error")
		}
	}

	{{- range $j := $tableDef.JSONValues }}
	if err := json.Unmarshal({{ $j.VarName }}JSON, {{ printf "&v.%s" $j.Field }}); err != nil {
		return nil, "", errors.Wrap(err, "failed to unmarshal {{ printf "v.%s" $j.Field }}")
	}
	{{- end }}

	return v, v.ID, nil
}

func (d *DB) scan{{ $tableDef.ObjectName }}s(rows *stdsql.Rows, txID string, skipFieldsCount uint) ([]*types.{{ $tableDef.ObjectName }}, []string, error) {
	vs := []*types.{{ $tableDef.ObjectName }}{}
	ids := []string{}
	for rows.Next() {
		v, id, err := d.scan{{ $tableDef.ObjectName }}(rows, skipFieldsCount)
		if err != nil {
			rows.Close()
			return nil, nil, errors.WithStack(err)
		}
		v.TxID = txID
		vs = append(vs, v)
		ids = append(ids, id)
	}
	if err := rows.Err(); err != nil {
		return nil, nil, errors.WithStack(err)
	}
	return vs, ids, nil
}

func (d *DB) {{ $tableDef.ObjectName }}Array() []any {
	a := []any{}

	{{- range $i, $mf := $tableDef.MapFields }}
	{{- if $mf.JSON }}
	a = append(a, new([]byte))
	{{- else }}
	a = append(a, new({{ $mf.FieldType }}))
	{{- end }}
	{{- end }}

	return a
}

func (d *DB) {{ $tableDef.ObjectName }}FromArray(a []any, txID string) (*types.{{ $tableDef.ObjectName }}, string, error) {
	v := &types.{{ $tableDef.ObjectName }}{}

	var vi any = v
	if x, ok := vi.(sqlg.Initer); ok {
		x.Init()
	}

	{{- range $i, $mf := $tableDef.MapFields }}
	{{- if eq $mf.JSON false }}
	{{ $mf.Field }} = *a[{{ $i }}].(*{{ $mf.FieldType }})
	{{- end }}
	{{- end }}

	if x, ok := vi.(sqlg.PreJSONSetupper); ok {
		if err := x.PreJSON(); err != nil {
			return nil, "", errors.Wrap(err, "prejson error")
		}
	}

	{{- range $i, $mf := $tableDef.MapFields }}
	{{- if $mf.JSON }}
	if err := json.Unmarshal(a[{{ $i }}].([]byte), {{ printf "&%s" $mf.Field }}); err != nil {
		return nil, "", errors.Wrap(err, "failed to unmarshal {{ printf "v.%s" $mf.Field }}")
	}
	{{- end }}
	{{- end }}

	v.TxID = txID

	return v, v.ID, nil
}
{{- end }}
`))
