package controller

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/AFASystems/presence/internal/pkg/common/appcontext"
	"github.com/AFASystems/presence/internal/pkg/model"
	"github.com/gorilla/mux"
	"github.com/segmentio/kafka-go"
)

func GetBeaconIds(appstate *appcontext.AppState) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		beacons := appstate.GetAllHttpResults()
		bIds := make([]string, len(beacons))
		i := 0
		for k := range beacons {
			bIds[i] = k
			i++
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		json.NewEncoder(w).Encode(bIds)
	}
}

func BeaconsListSingleController(appstate *appcontext.AppState) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		id := vars["beacon_id"]
		beacon, ok := appstate.GetHTTPResult(id)
		if !ok {
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(http.StatusNotFound)
			json.NewEncoder(w).Encode(map[string]string{"error": "Beacon not found"})
			return
		}

		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		json.NewEncoder(w).Encode(beacon)
	}
}

func BeaconsListController(appstate *appcontext.AppState) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		beacons := appstate.GetAllHttpResults()
		values := make([]model.HTTPResult, len(beacons))
		i := 0
		for k, v := range beacons {
			if k == "" {
				continue
			}
			values[i] = v
			i++
		}
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		json.NewEncoder(w).Encode(values)
	}
}

// Probably define value as interface and then reuse this writer in all of the functions
func sendKafkaMessage(writer *kafka.Writer, value *model.ApiUpdate, ctx context.Context) error {
	valueStr, err := json.Marshal(&value)
	if err != nil {
		fmt.Println("error in encoding: ", err)
		return err
	}
	msg := kafka.Message{
		Value: valueStr,
	}

	if err := writer.WriteMessages(ctx, msg); err != nil {
		fmt.Println("Error in sending kafka message: ", err)
		return err
	}

	return nil
}

func BeaconsDeleteController(writer *kafka.Writer, ctx context.Context, appstate *appcontext.AppState) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		beaconId := vars["beacon_id"]
		apiUpdate := model.ApiUpdate{
			Method: "DELETE",
			ID:     beaconId,
		}

		fmt.Printf("Sending DELETE beacon id: %s message\n", beaconId)

		if err := sendKafkaMessage(writer, &apiUpdate, ctx); err != nil {
			fmt.Println("error in sending Kafka DELETE message")
			http.Error(w, "Error in sending kafka message", 500)
			return
		}

		// If message is succesfully sent delete the beacon from the list
		appstate.RemoveHTTPResult(beaconId)
		w.Write([]byte("ok"))
	}
}

func BeaconAddService(writer *kafka.Writer, ctx context.Context, appstate *appcontext.AppState, beacon model.HTTPResult) error {
	id := beacon.ID
	appstate.UpdateHTTPResult(id, beacon)

	apiUpdate := model.ApiUpdate{
		Method: "POST",
		Beacon: beacon,
	}

	if err := sendKafkaMessage(writer, &apiUpdate, ctx); err != nil {
		return err
	}

	return nil
}

func BeaconsAddController(writer *kafka.Writer, ctx context.Context, appstate *appcontext.AppState) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		decoder := json.NewDecoder(r.Body)
		var inBeacon model.HTTPResult
		err := decoder.Decode(&inBeacon)

		if err != nil {
			http.Error(w, err.Error(), 400)
			return
		}

		if inBeacon.ID == "" {
			http.Error(w, "Beacon needs to include MAC addr", 400)
			return
		}

		if err := BeaconAddService(writer, ctx, appstate, inBeacon); err != nil {
			fmt.Println("error in sending Kafka POST message")
			http.Error(w, "Error in sending kafka message", 500)
			return
		}

		w.Write([]byte("ok"))
	}
}

func AddListOfBeaconsController(writer *kafka.Writer, ctx context.Context, appstate *appcontext.AppState) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		decoder := json.NewDecoder(r.Body)
		var inBeacons []model.HTTPResult
		if err := decoder.Decode(&inBeacons); err != nil {
			http.Error(w, err.Error(), 400)
			return
		}

		for _, v := range inBeacons {
			if v.ID == "" {
				fmt.Println("One of the beacons is missing MAC address")
				http.Error(w, "One of the beacons is missing MAC address", 400)
				return
			}

			if err := BeaconAddService(writer, ctx, appstate, v); err != nil {
				fmt.Println("error in sending Kafka POST message")
				http.Error(w, "Error in sending kafka message", 500)
				return
			}
		}

		w.Write([]byte("ok"))
	}
}
