package scraper

import (
	"encoding/binary"
	"fmt"
	"github.com/goburrow/serial"
	"github.com/rs/zerolog"
	"github.com/things-go/go-modbus"
	"math"
)

type Scraper struct {
	modbus modbus.Client
	logger zerolog.Logger
}

type Measurements struct {
	VoltageAN      float32
	VoltageBN      float32
	VoltageCN      float32
	VoltageAB      float32
	VoltageBC      float32
	VoltageCA      float32
	CurrentA       float32
	CurrentB       float32
	CurrentC       float32
	ApparantPowerA float32
	ApparantPowerB float32
	ApparantPowerC float32
	ActivePowerA   float32
	ActivePowerB   float32
	ActivePowerC   float32
	Frequency      float32
}

func New(address string, logger zerolog.Logger) (*Scraper, error) {
	p := modbus.NewRTUClientProvider(
		modbus.WithSerialConfig(serial.Config{
			Address:  address,
			BaudRate: 19200,
			DataBits: 8,
			StopBits: 2,
			Parity:   "N",
			Timeout:  modbus.SerialDefaultTimeout,
		}),
	)

	client := modbus.NewClient(p)
	err := client.Connect()
	if err != nil {

		return nil, fmt.Errorf("connecting modbus client: %w", err)
	}
	scraper := &Scraper{modbus: client, logger: logger}
	return scraper, nil
}

func (s *Scraper) GetMeasurments() (Measurements, error) {
	var err error
	measurement := Measurements{}

	measurement.VoltageAN, err = s.GetVoltageAN()
	if err != nil {
		s.logger.Err(err)
	}

	measurement.VoltageBN, err = s.GetVoltageBN()
	if err != nil {
		s.logger.Err(err)
	}

	measurement.VoltageCN, err = s.GetVoltageCN()
	if err != nil {
		s.logger.Err(err)
	}

	measurement.VoltageAB, err = s.GetVoltageAB()
	if err != nil {
		s.logger.Err(err)
	}

	measurement.VoltageBC, err = s.GetVoltageBC()
	if err != nil {
		s.logger.Err(err)
	}

	measurement.VoltageCA, err = s.GetVoltageCA()
	if err != nil {
		s.logger.Err(err)
	}

	measurement.CurrentA, err = s.GetCurrentA()
	if err != nil {
		s.logger.Err(err)
	}

	measurement.CurrentB, err = s.GetCurrentB()
	if err != nil {
		s.logger.Err(err)
	}

	measurement.CurrentC, err = s.GetCurrentC()
	if err != nil {
		s.logger.Err(err)
	}

	measurement.ApparantPowerA, err = s.GetApparentPowerA()
	if err != nil {
		s.logger.Err(err)
	}

	measurement.ApparantPowerB, err = s.GetApparentPowerB()
	if err != nil {
		s.logger.Err(err)
	}

	measurement.ApparantPowerC, err = s.GetApparentPowerC()
	if err != nil {
		s.logger.Err(err)
	}

	measurement.ActivePowerA, err = s.GetActivePowerA()
	if err != nil {
		s.logger.Err(err)
	}

	measurement.ActivePowerB, err = s.GetActivePowerB()
	if err != nil {
		s.logger.Err(err)
	}

	measurement.ActivePowerC, err = s.GetActivePowerC()
	if err != nil {
		s.logger.Err(err)
	}

	measurement.Frequency, err = s.GetFrequency()
	if err != nil {
		s.logger.Err(err)
	}

	return measurement, nil
}

//Unit: V
func (s *Scraper) GetVoltageAN() (float32, error) {
	results, err := s.modbus.ReadInputRegistersBytes(126, 1, 2)
	if err != nil {
		return 0, fmt.Errorf("reading Voltage A-N: %w", err)
	}
	return Float32fromBytes(results), nil
}

//Unit: V
func (s *Scraper) GetVoltageBN() (float32, error) {
	results, err := s.modbus.ReadInputRegistersBytes(126, 3, 2)
	if err != nil {
		return 0, fmt.Errorf("reading Voltage B-N: %w", err)
	}
	return Float32fromBytes(results), nil
}

//Unit: V
func (s *Scraper) GetVoltageCN() (float32, error) {
	results, err := s.modbus.ReadInputRegistersBytes(126, 5, 2)
	if err != nil {
		return 0, fmt.Errorf("reading Voltage C-N: %w", err)
	}
	return Float32fromBytes(results), nil
}

//Unit: V
func (s *Scraper) GetVoltageAB() (float32, error) {
	results, err := s.modbus.ReadInputRegistersBytes(126, 7, 2)
	if err != nil {
		return 0, fmt.Errorf("reading Voltage A-B: %w", err)
	}
	return Float32fromBytes(results), nil
}

//Unit: V
func (s *Scraper) GetVoltageBC() (float32, error) {
	results, err := s.modbus.ReadInputRegistersBytes(126, 9, 2)
	if err != nil {
		return 0, fmt.Errorf("reading Voltage B-C: %w", err)
	}
	return Float32fromBytes(results), nil
}

//Unit: V
func (s *Scraper) GetVoltageCA() (float32, error) {
	results, err := s.modbus.ReadInputRegistersBytes(126, 11, 2)
	if err != nil {
		return 0, fmt.Errorf("reading Voltage A-C: %w", err)
	}
	return Float32fromBytes(results), nil
}

//Unit: A
func (s *Scraper) GetCurrentA() (float32, error) {
	results, err := s.modbus.ReadInputRegistersBytes(126, 13, 2)
	if err != nil {
		return 0, fmt.Errorf("reading Current A: %w", err)
	}
	return Float32fromBytes(results), nil
}

//Unit: A
func (s *Scraper) GetCurrentB() (float32, error) {
	results, err := s.modbus.ReadInputRegistersBytes(126, 15, 2)
	if err != nil {
		return 0, fmt.Errorf("reading Current B: %w", err)
	}
	return Float32fromBytes(results), nil
}

//Unit: A
func (s *Scraper) GetCurrentC() (float32, error) {
	results, err := s.modbus.ReadInputRegistersBytes(126, 17, 2)
	if err != nil {
		return 0, fmt.Errorf("reading Current C: %w", err)
	}
	return Float32fromBytes(results), nil
}

//Unit: VA
func (s *Scraper) GetApparentPowerA() (float32, error) {
	results, err := s.modbus.ReadInputRegistersBytes(126, 19, 2)
	if err != nil {
		return 0, fmt.Errorf("reading Apparent Power A: %w", err)
	}
	return Float32fromBytes(results), nil
}

//Unit: VA
func (s *Scraper) GetApparentPowerB() (float32, error) {
	results, err := s.modbus.ReadInputRegistersBytes(126, 21, 2)
	if err != nil {
		return 0, fmt.Errorf("reading Apparent Power B: %w", err)
	}
	return Float32fromBytes(results), nil
}

//Unit: VA
func (s *Scraper) GetApparentPowerC() (float32, error) {
	results, err := s.modbus.ReadInputRegistersBytes(126, 23, 2)
	if err != nil {
		return 0, fmt.Errorf("reading Apparent Power C: %w", err)
	}
	return Float32fromBytes(results), nil
}

//Unit: VA
func (s *Scraper) GetActivePowerA() (float32, error) {
	results, err := s.modbus.ReadInputRegistersBytes(126, 25, 2)
	if err != nil {
		return 0, fmt.Errorf("reading Active Power A: %w", err)
	}
	return Float32fromBytes(results), nil
}

//Unit: VA
func (s *Scraper) GetActivePowerB() (float32, error) {
	results, err := s.modbus.ReadInputRegistersBytes(126, 27, 2)
	if err != nil {
		return 0, fmt.Errorf("reading Active Power B: %w", err)
	}
	return Float32fromBytes(results), nil
}

//Unit: VA
func (s *Scraper) GetActivePowerC() (float32, error) {
	results, err := s.modbus.ReadInputRegistersBytes(126, 25, 2)
	if err != nil {
		return 0, fmt.Errorf("reading Active Power C: %w", err)
	}
	return Float32fromBytes(results), nil
}

//Unit: Hz
func (s *Scraper) GetFrequency() (float32, error) {
	results, err := s.modbus.ReadInputRegistersBytes(126, 39, 2)
	if err != nil {
		return 0, fmt.Errorf("reading Freaquency: %w", err)
	}
	return Float32fromBytes(results), nil
}

func Float32fromBytes(bytes []byte) float32 {
	bits := binary.BigEndian.Uint32(bytes)
	float := math.Float32frombits(bits)
	return float
}
