package controllers

import (
	"database/sql"
	"errors"
	"github.com/jmoiron/sqlx"
	"go_project/internal/models"
	"log"
)

type (
	ListBookingParams struct {
		Limit  int `db:"limit" json:"limit"`
		Offset int `db:"offset" json:"offset"`
	}

	CreateBookingParams struct {
		IdTables    int    `db:"id_tables" json:"id_tables"`
		Reservation string `db:"reservation" json:"reservation"`
		TimeReserve string `db:"time_reserve" json:"time_reserve"`
		WhoBooked   string `db:"who_booked" json:"who_booked"`
	}

	UpdateBookingParams struct {
		Id          int    `db:"id" json:"id"`
		IdTables    int    `db:"id_tables" json:"id_tables"`
		Reservation string `db:"reservation" json:"reservation"`
		TimeReserve string `db:"time_reserve" json:"time_reserve"`
		WhoBooked   string `db:"who_booked" json:"who_booked"`
	}
)

func BookingById(db *sqlx.DB, id int) (*models.Booking, error) {
	rows, err := db.Query("SELECT * FROM Bookings WHERE id = ?", id)

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

	if isEmpty := !rows.Next(); isEmpty {
		return nil, nil
	}

	result := new(models.Booking)
	err = rows.Scan(&result.Id, &result.IdTables, &result.ReservationDatetime, &result.TimeReserve, &result.WhoBooked)

	return result, err
}

func AllBookings(db *sqlx.DB, params ListBookingParams) ([]models.Booking, error) {
	query := `SELECT * FROM Bookings ORDER BY id LIMIT $1 OFFSET $2`

	rows, err := db.Query(query, params.Limit, params.Offset)

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

	var result []models.Booking
	for rows.Next() {
		var currentBooking models.Booking

		err = rows.Scan(&currentBooking.Id, &currentBooking.IdTables, &currentBooking.ReservationDatetime, &currentBooking.TimeReserve, &currentBooking.WhoBooked)

		if err != nil {
			return nil, err
		}

		result = append(result, currentBooking)
	}
	return result, err
}

func CreateBooking(db *sqlx.DB, params CreateBookingParams) (*models.Booking, error) {
	query := `
        INSERT INTO Bookings (id_tables, reservation, time_reserve, who_booked) 
        VALUES ($1, $2, $3, $4)
        RETURNING id, id_tables, reservation, time_reserve, who_booked
    `

	var result models.Booking

	err := db.QueryRow(query,
		params.IdTables,
		params.Reservation,
		params.TimeReserve,
		params.WhoBooked,
	).Scan(
		&result.Id,
		&result.IdTables,
		&result.ReservationDatetime,
		&result.TimeReserve,
		&result.WhoBooked,
	)
	if err != nil {
		log.Printf("CreateBooking: Insertion operation of the Bookings object is failed: %v\n", err)
		return nil, err
	}

	log.Println("CreateBooking: Insertion operation of the Bookings object is successful")
	return &result, nil
}

func DeleteBookingById(db *sqlx.DB, id int) (sql.Result, error) {
	sqlResult, err := db.Exec("DELETE FROM Bookings WHERE id = ?", id)

	if err == nil {
		log.Println("DeleteBookingById: Deletion operation of the Bookings object is successful")
	}

	return sqlResult, err
}

func UpdateBooking(db *sqlx.DB, params UpdateBookingParams) (*models.Booking, error) {
	query := `
		UPDATE Bookings 
		SET id_tables = $1, reservation = $2, time_reserve = $3 
		WHERE id = $4 
		RETURNING id, id_tables, reservation, time_reserve, who_booked
	`

	result := &models.Booking{}
	err := db.QueryRow(query, params.IdTables, params.Reservation, params.TimeReserve, params.Id).
		Scan(&result.Id, &result.IdTables, &result.ReservationDatetime, &result.TimeReserve, &result.WhoBooked)
	if err != nil {
		switch {
		case err == sql.ErrNoRows:
			log.Printf("UpdateBooking: No rows affected for Booking ID %d", params.Id)
			return nil, errors.New("update failed: no rows affected")
		default:
			log.Printf("UpdateBooking: Error executing query for Booking ID %d: %v", params.Id, err)
			return nil, err
		}
	}

	log.Printf("UpdateBooking: Successfully updated Booking ID %d", result.Id)
	return result, nil
}
