package db

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
}

// ----------------------------------------------------------------------------
// Table: config
// ----------------------------------------------------------------------------

type Config struct {
	ConfigID int64
	Password []byte
}

const Config_SelectQuery = "SELECT ConfigID,Password FROM config"

func Config_Insert(
	tx TX,
	row *Config,
) (err error) {
	Config_Sanitize(row)
	if err = Config_Validate(row); err != nil {
		return err
	}

	_, err = tx.Exec("INSERT INTO config(ConfigID,Password) VALUES(?,?)", row.ConfigID, row.Password)
	return err
}

func Config_Update(
	tx TX,
	row *Config,
) (err error) {
	Config_Sanitize(row)
	if err = Config_Validate(row); err != nil {
		return err
	}

	result, err := tx.Exec("UPDATE config SET Password=? WHERE ConfigID=?", row.Password, row.ConfigID)
	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")
	}
}

func Config_UpdateFull(
	tx TX,
	row *Config,
) (err error) {
	Config_Sanitize(row)
	if err = Config_Validate(row); err != nil {
		return err
	}

	result, err := tx.Exec("UPDATE config SET Password=? WHERE ConfigID=?", row.Password, row.ConfigID)
	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")
	}
}

func Config_Delete(
	tx TX,
	ConfigID int64,
) (err error) {
	result, err := tx.Exec("DELETE FROM config WHERE ConfigID=?", ConfigID)
	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")
	}
}

func Config_Get(
	tx TX,
	ConfigID int64,
) (
	row *Config,
	err error,
) {
	row = &Config{}
	r := tx.QueryRow("SELECT ConfigID,Password FROM config WHERE ConfigID=?", ConfigID)
	err = r.Scan(&row.ConfigID, &row.Password)
	return
}

func Config_GetWhere(
	tx TX,
	query string,
	args ...any,
) (
	row *Config,
	err error,
) {
	row = &Config{}
	r := tx.QueryRow(query, args...)
	err = r.Scan(&row.ConfigID, &row.Password)
	return
}

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

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

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

// ----------------------------------------------------------------------------
// Table: sessions
// ----------------------------------------------------------------------------

type Session struct {
	SessionID  string
	CSRF       string
	SignedIn   bool
	CreatedAt  int64
	LastSeenAt int64
}

const Session_SelectQuery = "SELECT SessionID,CSRF,SignedIn,CreatedAt,LastSeenAt FROM sessions"

func Session_Insert(
	tx TX,
	row *Session,
) (err error) {
	Session_Sanitize(row)
	if err = Session_Validate(row); err != nil {
		return err
	}

	_, err = tx.Exec("INSERT INTO sessions(SessionID,CSRF,SignedIn,CreatedAt,LastSeenAt) VALUES(?,?,?,?,?)", row.SessionID, row.CSRF, row.SignedIn, row.CreatedAt, row.LastSeenAt)
	return err
}

func Session_Delete(
	tx TX,
	SessionID string,
) (err error) {
	result, err := tx.Exec("DELETE FROM sessions WHERE SessionID=?", SessionID)
	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")
	}
}

func Session_Get(
	tx TX,
	SessionID string,
) (
	row *Session,
	err error,
) {
	row = &Session{}
	r := tx.QueryRow("SELECT SessionID,CSRF,SignedIn,CreatedAt,LastSeenAt FROM sessions WHERE SessionID=?", SessionID)
	err = r.Scan(&row.SessionID, &row.CSRF, &row.SignedIn, &row.CreatedAt, &row.LastSeenAt)
	return
}

func Session_GetWhere(
	tx TX,
	query string,
	args ...any,
) (
	row *Session,
	err error,
) {
	row = &Session{}
	r := tx.QueryRow(query, args...)
	err = r.Scan(&row.SessionID, &row.CSRF, &row.SignedIn, &row.CreatedAt, &row.LastSeenAt)
	return
}

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

	return func(yield func(*Session, error) bool) {
		defer rows.Close()
		for rows.Next() {
			row := &Session{}
			err := rows.Scan(&row.SessionID, &row.CSRF, &row.SignedIn, &row.CreatedAt, &row.LastSeenAt)
			if !yield(row, err) {
				return
			}
		}
	}
}

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

// ----------------------------------------------------------------------------
// Table: networks
// ----------------------------------------------------------------------------

type Network struct {
	NetworkID int64
	Name      string
	Network   []byte
}

const Network_SelectQuery = "SELECT NetworkID,Name,Network FROM networks"

func Network_Insert(
	tx TX,
	row *Network,
) (err error) {
	Network_Sanitize(row)
	if err = Network_Validate(row); err != nil {
		return err
	}

	_, err = tx.Exec("INSERT INTO networks(NetworkID,Name,Network) VALUES(?,?,?)", row.NetworkID, row.Name, row.Network)
	return err
}

func Network_UpdateFull(
	tx TX,
	row *Network,
) (err error) {
	Network_Sanitize(row)
	if err = Network_Validate(row); err != nil {
		return err
	}

	result, err := tx.Exec("UPDATE networks SET Name=?,Network=? WHERE NetworkID=?", row.Name, row.Network, row.NetworkID)
	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")
	}
}

func Network_Delete(
	tx TX,
	NetworkID int64,
) (err error) {
	result, err := tx.Exec("DELETE FROM networks WHERE NetworkID=?", NetworkID)
	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")
	}
}

func Network_Get(
	tx TX,
	NetworkID int64,
) (
	row *Network,
	err error,
) {
	row = &Network{}
	r := tx.QueryRow("SELECT NetworkID,Name,Network FROM networks WHERE NetworkID=?", NetworkID)
	err = r.Scan(&row.NetworkID, &row.Name, &row.Network)
	return
}

func Network_GetWhere(
	tx TX,
	query string,
	args ...any,
) (
	row *Network,
	err error,
) {
	row = &Network{}
	r := tx.QueryRow(query, args...)
	err = r.Scan(&row.NetworkID, &row.Name, &row.Network)
	return
}

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

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

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

// ----------------------------------------------------------------------------
// Table: peers
// ----------------------------------------------------------------------------

type Peer struct {
	NetworkID  int64
	PeerIP     byte
	Version    int64
	APIKey     string
	Name       string
	PublicIP   []byte
	Port       uint16
	Relay      bool
	PubKey     []byte
	PubSignKey []byte
}

const Peer_SelectQuery = "SELECT NetworkID,PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey FROM peers"

func Peer_Insert(
	tx TX,
	row *Peer,
) (err error) {
	Peer_Sanitize(row)
	if err = Peer_Validate(row); err != nil {
		return err
	}

	_, err = tx.Exec("INSERT INTO peers(NetworkID,PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey) VALUES(?,?,?,?,?,?,?,?,?,?)", row.NetworkID, row.PeerIP, row.Version, row.APIKey, row.Name, row.PublicIP, row.Port, row.Relay, row.PubKey, row.PubSignKey)
	return err
}

func Peer_Update(
	tx TX,
	row *Peer,
) (err error) {
	Peer_Sanitize(row)
	if err = Peer_Validate(row); err != nil {
		return err
	}

	result, err := tx.Exec("UPDATE peers SET Version=?,Name=?,PublicIP=?,Port=?,Relay=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.Name, row.PublicIP, row.Port, row.Relay, row.NetworkID, row.PeerIP)
	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")
	}
}

func Peer_UpdateFull(
	tx TX,
	row *Peer,
) (err error) {
	Peer_Sanitize(row)
	if err = Peer_Validate(row); err != nil {
		return err
	}

	result, err := tx.Exec("UPDATE peers SET Version=?,APIKey=?,Name=?,PublicIP=?,Port=?,Relay=?,PubKey=?,PubSignKey=? WHERE NetworkID=? AND PeerIP=?", row.Version, row.APIKey, row.Name, row.PublicIP, row.Port, row.Relay, row.PubKey, row.PubSignKey, row.NetworkID, row.PeerIP)
	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")
	}
}

func Peer_Delete(
	tx TX,
	NetworkID int64,
	PeerIP byte,
) (err error) {
	result, err := tx.Exec("DELETE FROM peers WHERE NetworkID=? AND PeerIP=?", NetworkID, PeerIP)
	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")
	}
}

func Peer_Get(
	tx TX,
	NetworkID int64,
	PeerIP byte,
) (
	row *Peer,
	err error,
) {
	row = &Peer{}
	r := tx.QueryRow("SELECT NetworkID,PeerIP,Version,APIKey,Name,PublicIP,Port,Relay,PubKey,PubSignKey FROM peers WHERE NetworkID=? AND PeerIP=?", NetworkID, PeerIP)
	err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey)
	return
}

func Peer_GetWhere(
	tx TX,
	query string,
	args ...any,
) (
	row *Peer,
	err error,
) {
	row = &Peer{}
	r := tx.QueryRow(query, args...)
	err = r.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey)
	return
}

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

	return func(yield func(*Peer, error) bool) {
		defer rows.Close()
		for rows.Next() {
			row := &Peer{}
			err := rows.Scan(&row.NetworkID, &row.PeerIP, &row.Version, &row.APIKey, &row.Name, &row.PublicIP, &row.Port, &row.Relay, &row.PubKey, &row.PubSignKey)
			if !yield(row, err) {
				return
			}
		}
	}
}

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