package admin

import (
  "reflect"
  "fmt"
  "MY/webapp/data"
  _ "github.com/mattn/go-sqlite3"
  sq "github.com/Masterminds/squirrel"
)

func SearchTable(search string, table string, the_type reflect.Type, limit uint64, page uint64) ([]any, error) {

  var results []any
  like := fmt.Sprint("%", search, "%")

  builder := sq.Select("*").
    Limit(limit).
    Offset(limit * page).
    From(table)

  field_num := the_type.NumField()
  var or_clause sq.Or

  for i := 0; i < field_num; i++ {
    tag := the_type.Field(i).Tag.Get("db")
    or_clause = append(or_clause, sq.Like{tag: like})
  }

  builder = builder.Where(or_clause)
  sql_query, args, err := builder.ToSql()
  fmt.Println("-------------- SQL QUERY:", sql_query);

  if err != nil { return results, err }

  // BUG: refactor this to share a common func with SelectTable
  rows, err := data.DB.Queryx(sql_query, args...)
  if err != nil { return results, err }
  defer rows.Close()

  for rows.Next() {
    the_data := reflect.New(the_type).Interface()
    err = rows.StructScan(the_data)
    if err != nil { return results, err }

    results = append(results, the_data)
  }

  return results, rows.Err()
}

func SelectTable(table string, the_type reflect.Type, limit uint64, page uint64) ([]any, error) {
  var results []any
  sql_query, args, err := sq.Select("*").Limit(limit).Offset(limit * page).From(table).ToSql()
  if err != nil { return results, err }

  rows, err := data.DB.Queryx(sql_query, args...)
  if err != nil { return results, err }
  defer rows.Close()

  for rows.Next() {
    the_data := reflect.New(the_type).Interface()
    err = rows.StructScan(the_data)
    if err != nil { return results, err }

    results = append(results, the_data)
  }

  return results, rows.Err()
}

func Get(table string, the_type reflect.Type, id int64) (reflect.Value, error) {
  sql_query, args, err := sq.Select("*").From(table).Where(sq.Eq{"id": id}).ToSql()
  if err != nil { return reflect.New(nil), err }

  the_data := reflect.New(the_type)
  err = data.DB.Get(the_data.Interface(), sql_query, args...)

  // BUG: not sure if Elem or returning the reflect.New is better
  return the_data.Elem(), err
}

func Delete(table string, id int64) error {
  sql_query, args, err := sq.Delete(table).Where(sq.Eq{"id": id}).ToSql()
  if err != nil { return err }

  _, err = data.DB.Exec(sql_query, args...)
  return err
}

func Insert(table string, value reflect.Value) (int64, int64, error) {
  type_of := value.Type()
  field_num := value.NumField()
  var columns []string
  var values []any

  for i := 0; i < field_num; i++ {
    field := value.Field(i)
    tag := type_of.Field(i).Tag.Get("db")
    if tag == "id" { continue }
    columns = append(columns, tag)
    values = append(values, field.Interface())
  }

  builder := sq.Insert(table).Columns(columns...).Values(values...)
  sql_query, args, err := builder.ToSql()

  result, err := data.DB.Exec(sql_query, args...)
  id, err := result.LastInsertId()
  if err != nil { return -1, -1, err }

  count, err := result.RowsAffected()
  if err != nil { return id, -1, err }

  return id, count, err
}

func Update(table string, value reflect.Value) error {
  builder := sq.Update(table)

  type_of := value.Type()
  field_num := value.NumField()

  for i := 0; i < field_num; i++ {
    field := value.Field(i)
    tag := type_of.Field(i).Tag.Get("db")

    // skip update of id to avoid replacing it
    if tag == "id" { continue }

    builder = builder.Set(tag, field.Interface())
  }

  builder = builder.Where(sq.Eq{"id": value.FieldByName("Id").Interface()})
  sql_query, args, err := builder.ToSql()

  fmt.Println("UPDATE QUERY", sql_query, args)
  if err != nil { return err}

  _, err = data.DB.Exec(sql_query, args...)
  return err
}
