package controllers

import (
	"context"
	"log"
	"net/http"
	"ordr-api/dto"
	"ordr-api/queries"
	"ordr-api/utils"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgxpool"
)

func CreateOrder(pool *pgxpool.Pool) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		conn, conn_err := pool.Acquire(ctx)
		if conn_err != nil {
			log.Printf("CreateItem(): ERROR: Failed to connect... %s", conn_err.Error())
			ctx.AbortWithStatus(http.StatusInternalServerError)
			return
		}
		defer conn.Release()

		user_profile, _ := ctx.Get("user_profile")

		sub_id := user_profile.(dto.UserProfileResponse).Sub

		var current_user dto.UserResponse

		user_query_err := conn.QueryRow(context.Background(), queries.GET_CURRENT_USER_OBJECT, sub_id).Scan(&current_user.Id, &current_user.Name, &current_user.JobPosition, &current_user.Active, &current_user.Admin)

		if user_query_err != nil {
			log.Printf("CreateOrder(): ERROR Failed to query user... %s", user_query_err.Error())
			ctx.JSON(http.StatusInternalServerError, "CreateOrder(): ERROR Failed to query user")
			return
		}

		orderer := ctx.Query("orderer")
		date_due := ctx.Query("date_due")
		date_placed := ctx.Query("date_placed")

		if orderer == "" || date_due == "" {
			ctx.JSON(http.StatusBadRequest, "CreateOrder(): ERROR orderer or date_due not supplied")
			return
		}

		if date_placed == "" {
			currentTime := time.Now()
			date_placed = currentTime.Format(time.RFC3339)
		}

		_, placed_date_err := time.Parse(time.RFC3339, date_placed)
		if placed_date_err != nil {
			ctx.JSON(http.StatusBadRequest, "CreateOrder(): Bad time format")
			return
		}

		_, due_date_err := time.Parse(time.RFC3339, date_due)
		if due_date_err != nil {
			ctx.JSON(http.StatusBadRequest, "CreateOrder(): Bad Time format")
			return
		}

		_, exec_err := conn.Exec(context.Background(), queries.CREATE_ORDER, current_user.Id, orderer, date_due, date_placed)

		if exec_err != nil {
			ctx.JSON(http.StatusInternalServerError, "CreateOrder(): Failed to create order")
			log.Printf("CreateOrder(): ERROR... Failed to create order... %s", exec_err.Error())
			return
		}

		var order dto.OrderResponse
		order_query_err := conn.QueryRow(context.Background(), queries.GET_TOTAL_ORDER_FROM_ORDER_INFORMATION, current_user.Id, orderer, date_placed).Scan(&order.Id, &order.UserId, &order.Orderer, &order.DateDue, &order.DatePlaced, &order.AmountPaid, &order.OrderTotal, &order.AmountDue, &order.Filled, &order.Delivered)
		if order_query_err != nil {
			ctx.JSON(http.StatusInternalServerError, "CreateOrder(): Failed to create order")
			log.Printf("CreateOrder(): ERROR... failed to query order after creation ... %s", order_query_err.Error())
			return
		}

		ctx.JSON(http.StatusOK, order)
	}
}

func GetOrderByOrderId(pool *pgxpool.Pool) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		conn, conn_err := pool.Acquire(ctx)
		if conn_err != nil {
			log.Printf("GetOrderByOrderId(): ERROR: Failed to connect... %s", conn_err.Error())
			ctx.AbortWithStatus(http.StatusInternalServerError)
			return
		}
		defer conn.Release()

		order_id := ctx.Query("order_id")
		_, order_id_parse_err := strconv.ParseInt(order_id, 10, 64)
		if order_id == "" || order_id_parse_err != nil {
			ctx.JSON(http.StatusBadRequest, "GetOrderByOrderId(): ERROR... order_id not valid")
			return
		}

		row := conn.QueryRow(context.Background(), queries.GET_TOTAL_ORDER_FROM_ORDER_ID, order_id)

		var order_response dto.OrderResponse

		scan_err := row.Scan(&order_response.Id, &order_response.UserId, &order_response.Orderer, &order_response.DateDue, &order_response.DatePlaced, &order_response.AmountPaid, &order_response.OrderTotal, &order_response.AmountDue, &order_response.Filled, &order_response.Delivered)

		if scan_err != nil {
			if scan_err == pgx.ErrNoRows {
				ctx.JSON(http.StatusBadRequest, "GetOrderByOrderId(): ERROR... no order matches the query")
				return
			}
			ctx.JSON(http.StatusInternalServerError, "GetOrderByOrderId(): ERROR... internal server error")
			log.Printf("GetOrderByOrderId(): ERROR... Failed to scan row... %s", scan_err.Error())
			return
		}

		ctx.JSON(http.StatusOK, order_response)
	}
}

func GetOrderTable(pool *pgxpool.Pool) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		conn, conn_err := pool.Acquire(ctx)
		if conn_err != nil {
			log.Printf("GetOrderTable(): ERROR: Failed to connect... %s", conn_err.Error())
			ctx.AbortWithStatus(http.StatusInternalServerError)
			return
		}
		defer conn.Release()

		page := ctx.Query("page")
		page_int, page_parse_err := strconv.ParseInt(page, 10, 64)
		if page == "" || page_parse_err != nil {
			ctx.JSON(http.StatusBadRequest, "GetOrderTable(): Invalid page number")
			return
		}

		var table_query dto.OrderTableQuery

		bind_err := ctx.ShouldBindJSON(&table_query)
		if bind_err != nil {
			ctx.JSON(http.StatusBadRequest, "GetOrderTable(): ERROR... invalid query")
			return
		}

		filter := ctx.Query("filter")
		filter_int, filter_parse_err := strconv.ParseInt(filter, 10, 64)
		if filter == "" || filter_parse_err != nil {
			ctx.JSON(http.StatusBadRequest, "GetOrderTable(): Invalid Filter Options")
			return
		}

		query_string := utils.GetOrderTableQueryString(filter_int)
		rows, query_err := conn.Query(context.Background(), query_string, utils.PAGE_SIZE*page_int, utils.PAGE_SIZE, table_query.Orderer, table_query.DateDue, table_query.DatePlaced)
		if query_err != nil {
			ctx.JSON(http.StatusInternalServerError, "GetOrdertable(): ERROR... internal server error")
			log.Printf("GetOrderTable(): ERROR... Failed to query table...\nQUERY: %s\n %s", query_string, query_err.Error())
			return
		}
		defer rows.Close()

		var orders []dto.OrderResponse
		for rows.Next() {
			var order_response dto.OrderResponse
			scan_err := rows.Scan(&order_response.Id, &order_response.UserId, &order_response.Orderer, &order_response.DateDue, &order_response.DatePlaced, &order_response.AmountPaid, &order_response.OrderTotal, &order_response.AmountDue, &order_response.Filled, &order_response.Delivered)
			if scan_err != nil {
				ctx.JSON(http.StatusInternalServerError, "GetOrdertable(): ERROR... internal server error")
				log.Printf("GetOrderTable(): ERROR... Failed to scan query... %s", scan_err.Error())
				return
			}

			orders = append(orders, order_response)
		}

		ctx.JSON(http.StatusOK, orders)
	}
}

func DeleteOrder(pool *pgxpool.Pool) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		conn, conn_err := pool.Acquire(ctx)
		if conn_err != nil {
			log.Printf("DeleteOrder(): ERROR: Failed to connect... %s", conn_err.Error())
			ctx.AbortWithStatus(http.StatusInternalServerError)
			return
		}
		defer conn.Release()

		order_id := ctx.Query("order_id")
		order_id_int, parse_err := strconv.ParseInt(order_id, 10, 64)
		if parse_err != nil {
			ctx.JSON(http.StatusBadRequest, "DeleteOrder(): Invalid input")
			return
		}

		_, exec_err := conn.Exec(context.Background(), "DELETE FROM order_record WHERE id = $1", order_id_int)
		if exec_err != nil {
			ctx.JSON(http.StatusInternalServerError, "DeleteOrder(): ERROR... failed to delete order")
			log.Printf("DeleteOrder(): ERROR... Failed to delete order %s", exec_err.Error())
			return
		}
	}
}
