package main

import (
  "SampleApplicationGo/api"
  "SampleApplicationGo/handler"
  "context"
  "database/sql"
  "errors"
  "fmt"
  "github.com/GeertJohan/go.rice"
  "github.com/foolin/echo-template"

  "github.com/go-pg/pg"
  _ "github.com/go-sql-driver/mysql"
  "github.com/gocql/gocql"
  "github.com/gorilla/mux"

  "github.com/joho/godotenv"
  "github.com/labstack/echo"
  //"github.com/labstack/echo/middleware"
  _ "github.com/lib/pq"
  "go.mongodb.org/mongo-driver/bson"
  "go.mongodb.org/mongo-driver/mongo"
  "go.mongodb.org/mongo-driver/mongo/options"

  "html/template"
  "io"
  "log"
  "net/http"
  "os"
  _ "plugin"

  "time"
)

// Define the template registry struct
type TemplateRegistry struct {
  templates map[string]*template.Template
}

// Implement e.Renderer interface
func (t *TemplateRegistry) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
  tmpl, ok := t.templates[name]
  if !ok {
    err := errors.New("Template not found -> " + name)
    return err
  }
  return tmpl.ExecuteTemplate(w, "base.html", data)
}

type Pet struct {
  name    string
  owner   string
  species string
  sex     string
  birth   string
}
type Trainer struct {
  Name string
  Age  int
  City string
}

var (
  db *sql.DB
)

type Student struct {
  Rollno int64
  Name   string
}

func (s Student) String() string {
  return fmt.Sprintf("Student<%d %s>", s.Rollno, s.Name)
}

func pingAndQueryDB(db *sql.DB, ctx context.Context) {

  {
    status := " Connection is up"
    if err := db.PingContext(ctx); err != nil {
      status = " connection is down"
    }
    log.Println(status)
  }
  rows, err := db.Query("SELECT * FROM pet")
  if err != nil {
    log.Println(err)
  }
  defer rows.Close()

  bks := make([]*Pet, 0)
  for rows.Next() {
    bk := new(Pet)
    err := rows.Scan(&bk.name, &bk.owner, &bk.species, &bk.sex, &bk.birth)
    if err != nil {
      log.Println(err)
    }
    bks = append(bks, bk)
  }
  if err = rows.Err(); err != nil {
    log.Println(err)
  }

  for _, bk := range bks {
    fmt.Printf("%s, %s, %s,%s,%s \n", bk.name, bk.owner, bk.species, bk.sex, bk.birth)
  }

}

func callExample(ctx context.Context) {

  db, err := sql.Open("mysql", "root:cavisson@tcp(10.20.0.85:3306)/testdb")
  if err != nil {
    log.Println("Error opening DB")
    log.Println(err)
  }

  for i := 0; i < 10; i++ {
    pingAndQueryDB(db, ctx)
  }

}

func main() {

  err := godotenv.Load()
  if err != nil {
    log.Println("Error loading .env file")
  }
  // Echo instance
  e := echo.New()
  staticBox := rice.MustFindBox("static")
  staticFileServer := http.StripPrefix("/static/", http.FileServer(staticBox.HTTPBox()))
  e.GET("/static/*", echo.WrapHandler(staticFileServer))
  // Middleware
  /* e.Use(middleware.Logger())
  e.Use(middleware.Recover())*/
  e.Renderer = echotemplate.Default()
  // Instantiate a template registry with an array of template set
  // Ref: https://gist.github.com/rand99/808e6e9702c00ce64803d94abff65678
  templates := make(map[string]*template.Template)
  templates["home.html"] = template.Must(template.ParseFiles("view/home.html", "view/base.html"))
  templates["about.html"] = template.Must(template.ParseFiles("view/about.html", "view/base.html"))
  templates["page.html"] = template.Must(template.ParseFiles("view/page.html", "view/base.html"))
  templates["cassandra.html"] = template.Must(template.ParseFiles("view/cassandra.html", "view/base.html"))
  templates["mongodb.html"] = template.Must(template.ParseFiles("view/mongodb.html", "view/base.html"))
  templates["json.html"] = template.Must(template.ParseFiles("view/json.html", "view/base.html"))
  templates["postgres.html"] = template.Must(template.ParseFiles("view/postgres.html", "view/base.html"))
  templates["postpg.html"] = template.Must(template.ParseFiles("view/postpg.html", "view/base.html"))
  templates["mysql.html"] = template.Must(template.ParseFiles("view/mysql.html", "view/base.html"))
  templates["cats.html"] = template.Must(template.ParseFiles("view/cats.html", "view/base.html"))
  templates["httpcall.html"] = template.Must(template.ParseFiles("view/httpcall.html", "view/base.html"))
  templates["hello.html"] = template.Must(template.ParseFiles("view/hello.html", "view/base.html"))

  e.Renderer = &TemplateRegistry{
    templates: templates,
  }

  //grilla framework
  grouter := mux.NewRouter()
  grouter.HandleFunc("/mux/", handler.HttpHandler).Methods("GET", "PUT").Name("mux")

  gorillaRouterNames := map[string]string{
    "mux": "/mux/",
  }
  for name, url := range gorillaRouterNames {
    route := grouter.GetRoute(name)
    methods, _ := route.GetMethods()

    e.Match(methods, url, echo.WrapHandler(route.GetHandler()))

  }

  e.GET("/hello", Hello)

  // Route => handler
  e.GET("/", handler.HomeHandler)
  e.GET("/about", handler.AboutHandler)

  // Route => api
  e.GET("/api/get-full-name", api.GetFullName)
  e.POST("/api/post-full-name", api.PostFullName)
  e.GET("/json/abhi/karan/abhay", func(c echo.Context) error {
    return c.Render(http.StatusOK, "json.html", echo.Map{"title": "Page file title!!"})
  })

  e.GET("/page", func(c echo.Context) error {
    //render only file, must full name with extension
    return c.Render(http.StatusOK, "page.html", echo.Map{"title": "Page file title!!"})
  })
  e.GET("/mysql", mainAdmin)
  e.GET("/mongodb", mainmongo)
  e.GET("/cassandra", maincassandra)
  e.GET("/postgres", GetMovies)
  e.GET("/postpg", Postpg)
  e.GET("/cats", getabhi)
  e.GET("/httpcall", http_callout)

  // Start the Echo server
  e.Logger.Fatal(e.Start(":1323"))
}

func Hello(c echo.Context) error {
  return c.Render(http.StatusOK, "hello.html", echo.Map{"title": "Page file title!!"})
}

func getabhi(c echo.Context) error {
  //render with master
  catName := c.QueryParam("name")
  catType := c.QueryParam("type")
  log.Printf("\nname=%s,type=%s", catName, catType)
  return c.Render(http.StatusOK, "cats.html", echo.Map{"title": "Page file title!!"})
}
func mainAdmin(c echo.Context) error {
  req := c.Request()
  ctx := req.Context()
  callExample(ctx)
  return c.Render(http.StatusOK, "mysql.html", echo.Map{"title": "Page file title!!"})

}
func mainmongo(c echo.Context) error {
  req := c.Request()
  ctx := req.Context()

  // Set client options
  client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI("mongodb://10.20.0.85:27017?maxPoolSize=20&w=majority"))
  /*clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
    // Connect to MongoDB
    client, err := mongo.Connect(ctx, clientOptions)*/

  if err != nil {
    //    ErrorPrint("error come from client")
    log.Println(err)
  }

  // Check the connection
  err = client.Ping(ctx, nil)

  if err != nil {
    log.Println(err)
    //    ErrorPrint("Error : come from Ping")
  }

  //log.Println("Connected to MongoDB!")

  collection := client.Database("test").Collection("trainers")

  ash := Trainer{"Ash", 10, "Pallet Town"}

  _, err = collection.InsertOne(ctx, ash)
  if err != nil {
    log.Println(err)
  }

  //logger.TracePrint("Inserted a single document: ", insertResult.InsertedID)
  //log.Println("Inserted a single document: ", insertResult.InsertedID)
  filter := bson.D{{"name", "Ash"}}

  var result Trainer

  err = collection.FindOne(ctx, filter).Decode(&result)
  if err != nil {
    log.Println(err)
  }
  testinstrument()
  //logger.TracePrint("Found a single document:%+v ", result)
  //log.Println("Found a single document:%+v ", result)
  return c.Render(http.StatusOK, "mongodb.html", echo.Map{"title": "Page file title!!"})

}
func testinstrument() {
  log.Println("inside testinstrument")
  time.Sleep(1 * time.Second)
}

func maincassandra(c echo.Context) error {

  req := c.Request()
  ctx := req.Context()

  callGOCql(ctx)
  return c.Render(http.StatusOK, "cassandra.html", echo.Map{"title": "Page file title!!"})

}

var Session *gocql.Session

type Emp struct {
  id string

  firstName string

  lastName string

  age int
}

func createEmp(emp Emp) {
  fmt.Println(" **** Creating new emp ****\n", emp)
  var err error
  cluster := gocql.NewCluster("127.0.0.1")

  cluster.Keyspace = "code2succeed"

  Session, err = cluster.CreateSession()

  if err != nil {
    panic(err)
  }
  if err := Session.Query("INSERT INTO emps(empid, first_name, last_name, age) VALUES(?, ?, ?, ?)",
    emp.id, emp.firstName, emp.lastName, emp.age).Exec(); err != nil {
    fmt.Println(err)
  }
}

func callGOCql(ctx context.Context) {

  emp1 := Emp{"E-1", "Anupam", "Raj", 20}

  emp2 := Emp{"E-2", "Rahul", "Anand", 30}

  createEmp(emp1)

  createEmp(emp2)
}

func setupDB() *sql.DB {

  DB_USER, DB_PASSWORD := os.Getenv("POSTGRES_USER"), os.Getenv("POSTGRES_PASS")
  if DB_USER == "" && DB_PASSWORD == "" {
    DB_USER = "postgres"
    DB_PASSWORD = "12345678"
  }

  DB_NAME := "movies"

  dbinfo := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable", DB_USER, DB_PASSWORD, DB_NAME)
  db, err := sql.Open("postgres", dbinfo)

  checkErr(err)
  // defer db.Close()
  return db
}

type Movie struct {
  MovieID   string `json:"movieid"`
  MovieName string `json:"moviename"`
}

type JsonResponse struct {
  Type string  `json:"type"`
  Data []Movie `json:"data"`
  // Message string `json:"message"`
}

func checkErr(err error) {
  if err != nil {
    panic(err)
  }
}

func GetMovies(c echo.Context) error {
  db := setupDB()

  // printMessage("Getting movies...")
  defer db.Close()
  rows, err := db.Query("SELECT * FROM movies")

  checkErr(err)

  var movies []Movie

  for rows.Next() {
    var id int
    var movieID string
    var movieName string

    err = rows.Scan(&id, &movieID, &movieName)

    checkErr(err)

    movies = append(movies, Movie{MovieID: movieID, MovieName: movieName})
  }

  return c.Render(http.StatusOK, "postgres.html", echo.Map{"title": "Page file title!!"})

}

func Postpg(c echo.Context) error {

  DB_USER, DB_PASSWORD := os.Getenv("POSTGRES_USER"), os.Getenv("POSTGRES_PASS")
  if DB_USER == "" && DB_PASSWORD == "" {
    DB_USER = "postgres"
    DB_PASSWORD = "12345678"
  }
  db := pg.Connect(&pg.Options{
    User:     DB_USER,
    Password: DB_PASSWORD,
    //  Database: "postgres",
  })

  defer db.Close()

  /*
      _, err := db.Exec(`INSERT INTO "student" ("rollno", "name") VALUES (4,'Ajay')` )
     checkErr(err)
  */
  var student []Student
  //err := db.Model(&student).Select()
  _, err := db.Query(&student, `SELECT * FROM student`)
  checkErr(err)

  var stu Student
  _, err = db.Query(&stu, `SELECT * FROM student WHERE Rollno = ?`, 1)
  checkErr(err)
  fmt.Println(stu)

  //      _, err = db.Model(Win).Insert()
  //       _,err = db.Model(Win).Returning("*").Insert()
  fmt.Println(student)
  return c.Render(http.StatusOK, "postpg.html", echo.Map{"title": "Page file title!!"})

}

func http_callout(c echo.Context) error {

  client := &http.Client{}
  req, err := http.NewRequest("GET", "https://www.cavisson.com/", nil)
  // reqire context of parent function amd pass it into newrequest
  //      ParentReq := c.Request()
  //      ctx := ParentReq.Context()
  //      req = req.WithContext(ctx)

  if err != nil {
    log.Print("Error : creating new request.")
  }
  resp, err2 := client.Do(req)
  if err2 != nil {
    log.Print("Error : reading response. ")
  }
  if resp != nil {
    defer resp.Body.Close()
  }
  //Testfunc1(2)
  return c.Render(http.StatusOK, "httpcall.html", echo.Map{"title": "Page file title!!"})

}

