package main

import (
	"context"
	"os"
	"strings"
	"time"

	// "errors"
	"fmt"
	// "io"
	// "io/ioutil"

	// "net"
	"database/sql"
	"html/template"
	"log"
	"net/http"
	"sync"
	"syscall"

	"os/signal"

	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/websocket"
)

var tmpl *template.Template
var db 	 *sql.DB

const (
	keyServerAddr = "serverAddr"
	dbHost = "172.17.0.2"
	dbUser = "root"
	dbPort = 3306
	dbPassword = "root"
	dbName = "gomatic"
)

// type Agent struct {
// 	agentId int
// 	agentName string
// 	initialContact string
// 	lastContact string
// }

type Agent struct {
	AgentID        int    `json:"agentId"`
	AgentName      string `json:"agentName"`
	InitialContact string `json:"initialContact"`
	LastContact    string `json:"lastContact"`
}

func init() {
	tmpl, _ = template.ParseGlob("templates/*.html")
}

func renderTemplate(w http.ResponseWriter, tmpl string, data interface{}) {
	t, err := template.ParseFiles(tmpl)
		if err != nil {
		log.Printf("Failed to load template %s: %v", tmpl, err)
		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
		return
	}
	if err := t.Execute(w, data); err != nil {
		log.Printf("Failed to render template %s: %v", tmpl, err)
		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
		return
	}
}

func initDB () {
	var err  error
	dbOpen := fmt.Sprintf("%s:%s@(%s:%d)/%s?parseTime=true", dbUser,dbPassword, dbHost, dbPort, dbName)
	db, err = sql.Open("mysql", dbOpen)
	if err != nil {
		log.Fatal(err)
	}

	if err = db.Ping(); err != nil {
		log.Fatal(err)
	}
	log.Println("Connected to database")
}

// func fetchAgents(w http.ResponseWriter, r *http.Request) {
// 	agents, _ := getAgents(db)

// 	log.Printf("%s",agents)
// 	tmpl.ExecuteTemplate(w, "agentList", agents)
// }

// func getAgents(dbPointer *sql.DB) ([]Agent, error) {
// 	query := "Select agentId, agentName, initialContact, lastContact from agents"
// 	rows, err := dbPointer.Query(query)

// 	if err != nil {
// 		return nil, err
// 	}
// 	defer rows.Close()

// 	var agents []Agent
// 	for rows.Next() {
// 		var agent Agent

// 		rowErr := rows.Scan(&agent.AgentID, &agent.AgentName, &agent.InitialContact, &agent.LastContact)
// 		if rowErr != nil {
// 			return nil, err
// 		}
// 		agents = append(agents, agent)
// 	}

// 	if err = rows.Err(); err != nil {
// 		return nil, err
// 	}

// 	return agents, nil
// 	// return agents, rows.Err()
// }

// func getRoot(w http.ResponseWriter, r *http.Request) {
// 	ctx := r.Context()

// 	hasFirst := r.URL.Query().Has("first")
// 	first := r.URL.Query().Get("first")
// 	hasSecond := r.URL.Query().Has("second")
// 	second := r.URL.Query().Get("second")

// 	body, err := ioutil.ReadAll(r.Body)

// 	if err != nil {
// 		fmt.Printf("Could not read body: %s\n", err)
// 	}

// 	fmt.Printf("%s: got / request. first(%t)=%s, second(%t)=%s body:\n%s\n",
// 		ctx.Value(keyServerAddr),
// 		hasFirst, first,
// 		hasSecond, second,
// 		body,
// 	)

// 	// fmt.Printf("%s: got / request\n", ctx.Value(keyServerAddr))
// 	io.WriteString(w, "This is my website!\n")
// }

// func getHello(w http.ResponseWriter, r *http.Request) {
// 	ctx := r.Context()

// 	fmt.Printf("%s: go /hello request\n", ctx.Value(keyServerAddr))

// 	myName := r.PostFormValue("myName")
// 	if myName == "" {
// 		w.Header().Set("x-missing-field", "myName")
// 		w.WriteHeader(http.StatusBadRequest)
// 		return
// 	}

// 	io.WriteString(w, fmt.Sprintf("Hello, %s!\n", myName))
// }

func getHomepage(w http.ResponseWriter, r *http.Request) {
	tmpl.ExecuteTemplate(w, "index.html", nil)
}

func agentsHandler(w http.ResponseWriter, r *http.Request) {
	// if r.URL.Path == "/agents" {
	// 	if r.Method == http.MethodGet{
	// 		agents, err := getAgentsFromDB()
	// 		if err != nil {
	// 			http.Error(w, "Failed to fetch agents", http.StatusInternalServerError)
	// 			return
	// 		}
	// 		renderTemplate(w, "templates/partials/agent_list.html", agents)
	// 	} else {
	// 		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
	// 	}
	// 	return
	// }

	parts := strings.Split(strings.TrimPrefix(r.URL.Path, "/agents/"), "/")
	agentId := ""
	if len(parts) > 0 && parts[0] != "" {
		agentId = parts[0]
	}

	// if len (parts) < 1 || parts[0] == "" {
	// 	http.Error(w, "Agent ID required", http.StatusBadRequest)
	// 	return
	// }

	// agentId := parts[0]

	switch r.Method {
	case http.MethodDelete:
		deleteAgent(w, r, agentId)
	case http.MethodGet:
		if agentId == "" {
			listAgents(w, r)
		} else {
			getAgent(w, r, agentId)
		}
	case http.MethodPost:
		createAgent(w, r)
	case http.MethodPut:
		updateAgent(w, r, agentId)
	default:
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
	}
}

func listAgents(w http.ResponseWriter, r *http.Request) {
	agents, err := getAgentsFromDB()
	if err != nil {
		http.Error(w, "Failed to fetch agents", http.StatusInternalServerError)
		return
	}

	renderTemplate(w, "templates/partials/agent_list.html", agents)
}

func deleteAgent(w http.ResponseWriter, r *http.Request, agentID string) {
	query := "DELETE FROM agents WHERE agentId = ?"
	_, err := db.Exec(query, agentID)
	if err != nil {
		http.Error(w, "Failed to delete agent", http.StatusInternalServerError)
		return
	}

	agents, err := getAgentsFromDB()
	if err != nil {
		http.Error(w, "Failed to fetch agents", http.StatusInternalServerError)
		return
	}

	renderTemplate(w, "templates/partials/agent_list.html", agents)
}

func createAgent(w http.ResponseWriter, r * http.Request) {
	err := r.ParseForm()
	if err != nil {
		http.Error(w, "Invalid form data", http.StatusBadRequest)
	}

	agentName := r.FormValue("agentName")
	agentId := r.FormValue("agentId")
	// initalContact := r.FormValue("initialContact")
	// lastContact := r.FormValue("lastContact")

	query := "INSERT INTO agents (agentId, agentName, initialContact, lastContact) VALUES (?, ?, NOW(), NOW())"
	_, err = db.Exec(query, agentId, agentName)
	if err != nil {
		http.Error(w, "Failed to create agent", http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusCreated)
	listAgents(w, r)
}

func updateAgent(w http.ResponseWriter, r *http.Request, agentId string) {
	err := r.ParseForm()
	if err != nil {
		http.Error(w, "Invalid form data", http.StatusBadRequest)
		return
	}

	agentName := r.FormValue("agentName")
	lastContact := r.FormValue("lastContact")

	query := "UPDATE agents SET agentName = ?, lastContact = ? where agentId = ?"
	_, err = db.Exec(query, agentName, lastContact, agentId)
	if err != nil {
		http.Error(w, "Failed to update agent", http.StatusInternalServerError)
		return
	}

	listAgents(w, r,)
}

func getAgentsFromDB() ([]Agent, error) {
	query := "SELECT agentId, agentName, initialContact, lastContact FROM agents"
	rows, err := db.Query(query)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var agents []Agent
	for rows.Next() {
		var agent Agent
		err := rows.Scan(&agent.AgentID, &agent.AgentName, &agent.InitialContact, &agent.LastContact)
		if err != nil {
			return nil, err
		}
		agents = append(agents, agent)
	}
	return agents, rows.Err()
}

// func agentHandler(w http.ResponseWriter, r *http.Request) {
// 	parts := strings.Split(strings.TrimPrefix(r.URL.Path, "/agents/"), "/")
// 	if len (parts) < 1 || parts[0] == "" {
// 		http.Error(w, "Agent ID required", http.StatusBadRequest)
// 		return
// 	}

// 	agentId := parts[0]

// 	switch r.Method {
// 		case http.MethodGet:
// 			getAgent(w, r, agentId)
// 		// case http.MethodPost:
// 		// 	createAgent(w, r, agentId)
// 		// case http.MethodPut:
// 		// 	updateAgent(w, r, agentId)
// 		// case http.MethodDelete:
// 		// 	deleteAgent(w, r, agentId)
// 		default:
// 			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
// 		}
// }

func getAgent(w http.ResponseWriter, r *http.Request, agentId string) {
	query := "Select agentId, agentName, initialContact, lastContact from agents where agentId = ?"
	var agent Agent
	err := db.QueryRow(query, agentId).Scan(&agent.AgentID, &agent.AgentName, &agent.InitialContact, &agent.LastContact)
	if err == sql.ErrNoRows {
		http.Error(w, "Agent not found", http.StatusNotFound)
		return
	} else if err != nil {
		http.Error(w, "Failed to fetch agent", http.StatusInternalServerError)
		return
	}

	// return agent, nil
	// TODO: Add agent_detail.html
	renderTemplate(w, "templates/partials/agent_detail.html", agent)

}

type webSocketHandler struct {
	upgrader websocket.Upgrader
}

func (wsh webSocketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	c, err := wsh.upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Printf("Error %s when upgrading connection to websocket", err)
		return
	}
	defer c.Close()

	for {
		mt, message, err := c.ReadMessage()
		if err != nil {
			log.Printf("Error reading: message: %s", err)
		}

		log.Printf("Received message: %s", message)
		if err = c.WriteMessage(mt, message); err !=nil {
			log.Printf("Error writing the message: %s", err)
			break
		}
	}
}

func main() {

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	var wg sync.WaitGroup

	webSocketHandler := webSocketHandler {
		upgrader: websocket.Upgrader{},
	}

	webSocketMux := http.NewServeMux()
	webSocketMux.Handle("/data", webSocketHandler)
	websocketServer := &http.Server{
		Addr: ":5555",
		Handler: webSocketMux,
	}

	webMux := http.NewServeMux()
	webMux.HandleFunc("/", getHomepage)
	// webMux.HandleFunc("/index", getRoot)
	// webMux.HandleFunc("/hello", getHello)
	// webMux.HandleFunc("/agents", fetchAgents)
	webMux.HandleFunc("/agents", agentsHandler)
	// webMux.HandleFunc("/newagentform", getAgentForm)
	// webMux.HandleFunc("/getagentupdateform/{agentId}", getAgentUpdateForm)
	webMux.HandleFunc("/agents/{agentId}", agentsHandler)
	// webMux.HandleFunc


	initDB()
	defer db.Close()

	webServer := &http.Server {
		Addr: ":3333",
		Handler: webMux,
		// BaseContext: func(l net.Listener) context.Context {
		// 	ctx = context.WithValue(ctx, keyServerAddr, l.Addr().String())
		// 	return ctx
		// },
	}

	wg.Add(1)
	go func() {
		defer wg.Done()
		log.Println("Websocket server is running on port 5555")
		if err := websocketServer.ListenAndServe(); err != http.ErrServerClosed {
			log.Fatalf("Websocket server failed: %s", err)
		}
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		log.Println("Web server is running on port 3333")

		if err := webServer.ListenAndServe(); err != http.ErrServerClosed {
			log.Fatalf("Web server failed: %s", err)
		}
	}()

	shutdownCh := make(chan os.Signal, 1)
	signal.Notify(shutdownCh, os.Interrupt, syscall.SIGTERM)

	<-shutdownCh
	log.Println("Shutdown signal received")

	shutdownCtx, shutdownCancel := context.WithTimeout(ctx, 10*time.Second)
	defer shutdownCancel()

	if err := websocketServer.Shutdown(shutdownCtx); err != nil {
		log.Printf("error shutting down websocket server: %s", err)
	}

	if err := webServer.Shutdown(shutdownCtx); err != nil {
		log.Printf("Error shutting down web server: %s", err)
	}

	wg.Wait()
	log.Println("All servers stopped")
}
