package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"time"

	"github.com/AFASystems/presence/internal/pkg/config"
	"github.com/AFASystems/presence/internal/pkg/httpserver"
	"github.com/AFASystems/presence/internal/pkg/model"
	"github.com/AFASystems/presence/internal/pkg/mqtt_client"
	"github.com/AFASystems/presence/internal/pkg/persistence"
	"github.com/boltdb/bolt"
	"github.com/gorilla/websocket"
	"github.com/yosssi/gmq/mqtt"
	"github.com/yosssi/gmq/mqtt/client"
)

func main() {
	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, os.Interrupt)
	cfg := config.Load()

	db, err := bolt.Open("presence.db", 0644, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	model.Db = db

	cli := client.New(&client.Options{
		ErrorHandler: func(err error) {
			fmt.Println(err)
		},
	})

	defer cli.Terminate()

	fmt.Println("host: ", cfg.MQTTHost, " Client ID: ", cfg.MQTTClientID, "user: ", cfg.MQTTUser)

	err = cli.Connect(&client.ConnectOptions{
		Network:  "tcp",
		Address:  cfg.MQTTHost,
		ClientID: []byte(cfg.MQTTClientID),
		// UserName: nil,
		// Password: nil,
		UserName: []byte(cfg.MQTTUser),
		Password: []byte(cfg.MQTTPass),
	})

	if err != nil {
		fmt.Println("Error comes from here")
		panic(err)
	}

	ctx := &model.AppContext{
		HTTPResults: model.HTTPResultsList{
			HTTPResults: model.HTTPLocationsList{Beacons: []model.HTTPLocation{}},
		},
		Beacons: model.BeaconsList{
			Beacons: make(map[string]model.Beacon),
		},
		ButtonsList: make(map[string]model.Button),
		Settings: model.Settings{
			Location_confidence:  4,
			Last_seen_threshold:  15,
			Beacon_metrics_size:  30,
			HA_send_interval:     5,
			HA_send_changes_only: false,
		},
		Clients:    make(map[*websocket.Conn]bool),
		Broadcast:  make(chan model.Message, 100),
		Locations:  model.LocationsList{Locations: make(map[string]model.Location)},
		LatestList: model.LatestBeaconsList{LatestList: make(map[string]model.Beacon)},
	}

	persistence.LoadState(model.Db, ctx)
	incomingChan := mqtt_client.IncomingMQTTProcessor(1*time.Second, cli, model.Db, ctx)

	err = cli.Subscribe(&client.SubscribeOptions{
		SubReqs: []*client.SubReq{
			&client.SubReq{
				TopicFilter: []byte("publish_out/#"),
				QoS:         mqtt.QoS0,
				Handler: func(topicName, message []byte) {
					msgStr := string(message)
					t := strings.Split(string(topicName), "/")
					hostname := t[1]

					if strings.HasPrefix(msgStr, "[") {
						var readings []model.RawReading
						err := json.Unmarshal(message, &readings)
						if err != nil {
							log.Printf("Errore parsing JSON: %v", err)
							return
						}

						for _, reading := range readings {
							if reading.Type == "Gateway" {
								continue
							}
							incoming := model.Incoming_json{
								Hostname:         hostname,
								MAC:              reading.MAC,
								RSSI:             int64(reading.RSSI),
								Data:             reading.RawData,
								HB_ButtonCounter: parseButtonState(reading.RawData),
							}
							incomingChan <- incoming
						}
					} else {
						s := strings.Split(string(message), ",")
						if len(s) < 6 {
							log.Printf("Messaggio CSV non valido: %s", msgStr)
							return
						}

						rawdata := s[4]
						buttonCounter := parseButtonState(rawdata)
						if buttonCounter > 0 {
							incoming := model.Incoming_json{}
							i, _ := strconv.ParseInt(s[3], 10, 64)
							incoming.Hostname = hostname
							incoming.Beacon_type = "hb_button"
							incoming.MAC = s[1]
							incoming.RSSI = i
							incoming.Data = rawdata
							incoming.HB_ButtonCounter = buttonCounter

							read_line := strings.TrimRight(string(s[5]), "\r\n")
							it, err33 := strconv.Atoi(read_line)
							if err33 != nil {
								fmt.Println(it)
								fmt.Println(err33)
								os.Exit(2)
							}
							incomingChan <- incoming
						}
					}
				},
			},
		},
	})
	if err != nil {
		panic(err)
	}

	fmt.Println("CONNECTED TO MQTT")
	fmt.Println("\n ")
	fmt.Println("Visit http://" + cfg.HTTPAddr + " on your browser to see the web interface")
	fmt.Println("\n ")

	go httpserver.StartHTTPServer(cfg.HTTPAddr, ctx)

	<-sigc

	if err := cli.Disconnect(); err != nil {
		panic(err)
	}
}

func parseButtonState(raw string) int64 {
	raw = strings.ToUpper(raw)

	if strings.HasPrefix(raw, "0201060303E1FF12") && len(raw) >= 38 {
		buttonField := raw[34:38]
		if buttonValue, err := strconv.ParseInt(buttonField, 16, 64); err == nil {
			return buttonValue
		}
	}

	if strings.HasPrefix(raw, "02010612FF590") && len(raw) >= 24 {
		counterField := raw[22:24]
		buttonState, err := strconv.ParseInt(counterField, 16, 64)
		if err == nil {
			return buttonState
		}
	}

	return 0
}
