package main

import (
	"bytes"
	"compress/flate"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/fiskerinc/cloud-services/pkg/logger"
	"github.com/fiskerinc/cloud-services/pkg/utils/app"
	"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsflate"
)

var (
	manufacturerURL = envtool.GetEnv("MANUFACTURER_URL", "http://gateway.cloud-services.svc.cluster.local:8077/manufacture/manufacturer")
	gatewayWSURL    = envtool.GetEnv("GATEWAY_WS_URL", "ws://gateway.cloud-services.svc.cluster.local:8077/session")
	apiKey          = envtool.GetEnv("API_KEY", "")
	vinPrefix       = envtool.GetEnv("VIN_PREFIX", "VIRTUAL")
	sendInterval    = envtool.GetEnvInt("SEND_INTERVAL_MS", 1000)
)

func main() {
	app.Setup("virtual-vehicle", func() {})

	// Generate random VIN
	vin := generateVIN(vinPrefix)
	logger.Info().Str("vin", vin).Msg("Generated VIN")

	// Register vehicle and get certificates
	logger.Info().Str("url", manufacturerURL).Msg("Registering with manufacturer")
	cert, key, err := registerVehicle(vin)
	if err != nil {
		logger.Error().Err(err).Msg("Failed to register vehicle")
		time.Sleep(time.Second) // Allow log to flush
		os.Exit(1)
	}
	logger.Info().Str("vin", vin).Msg("Vehicle registered successfully")

	// Load TLS certificate
	tlsCert, err := tls.X509KeyPair([]byte(cert), []byte(key))
	if err != nil {
		logger.Error().Err(err).Msg("Failed to load TLS certificate")
		os.Exit(1)
	}

	// Connect to gateway
	logger.Info().Str("url", gatewayWSURL).Msg("Connecting to gateway")
	conn, err := connectToGateway(vin, tlsCert)
	if err != nil {
		logger.Fatal().Err(err).Msg("Failed to connect to gateway")
	}
	defer conn.Close()
	logger.Info().Str("vin", vin).Msg("Connected to gateway")

	// Handle shutdown
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// Start sending telemetry
	ticker := time.NewTicker(time.Duration(sendInterval) * time.Millisecond)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			if err := sendTelemetry(conn, vin); err != nil {
				logger.Error().Err(err).Msg("Failed to send telemetry")
				return
			}
		case <-sigChan:
			logger.Info().Msg("Shutting down")
			return
		}
	}
}

type ManufacturerResponse struct {
	Certificates []Certificate `json:"certificates"`
}

type Certificate struct {
	Type       string `json:"type"`
	PublicKey  string `json:"public_key"`
	PrivateKey string `json:"private_key"`
}

func registerVehicle(vin string) (cert, key string, err error) {
	payload := map[string]string{"vin": vin}
	body, _ := json.Marshal(payload)

	logger.Debug().Str("payload", string(body)).Msg("Creating request")
	
	req, err := http.NewRequest("POST", manufacturerURL, bytes.NewReader(body))
	if err != nil {
		return "", "", fmt.Errorf("create request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")
	if apiKey != "" {
		req.Header.Set("Api-Key", apiKey)
		logger.Debug().Msg("API key set")
	} else {
		logger.Warn().Msg("No API key configured")
	}

	logger.Debug().Msg("Sending HTTP request")
	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return "", "", fmt.Errorf("http request: %w", err)
	}
	defer resp.Body.Close()
	
	logger.Debug().Int("status", resp.StatusCode).Msg("Got response")

	if resp.StatusCode >= 300 {
		body, _ := io.ReadAll(resp.Body)
		return "", "", fmt.Errorf("manufacturer API returned %d: %s", resp.StatusCode, string(body))
	}

	var mfgResp ManufacturerResponse
	if err := json.NewDecoder(resp.Body).Decode(&mfgResp); err != nil {
		return "", "", err
	}

	for _, c := range mfgResp.Certificates {
		if c.Type == "TBOX" {
			return c.PublicKey, c.PrivateKey, nil
		}
	}

	return "", "", fmt.Errorf("no TBOX certificate found")
}

func connectToGateway(vin string, tlsCert tls.Certificate) (*wsConn, error) {
	dialer := ws.Dialer{
		TLSConfig: &tls.Config{
			Certificates:       []tls.Certificate{tlsCert},
			InsecureSkipVerify: true,
		},
		Header: ws.HandshakeHeaderHTTP(http.Header{
			"User-Agent": []string{fmt.Sprintf("Fisker Ocean T.Rex 1.0.0 %s", vin)},
		}),
	}

	conn, _, _, err := dialer.Dial(nil, gatewayWSURL)
	if err != nil {
		return nil, err
	}

	return &wsConn{conn: conn}, nil
}

type wsConn struct {
	conn   net.Conn
	writer *wsflate.Writer
}

func (w *wsConn) Close() error {
	return w.conn.Close()
}

func (w *wsConn) Write(data []byte) error {
	// Compress the data
	var buf bytes.Buffer
	fw, _ := flate.NewWriter(&buf, flate.BestSpeed)
	fw.Write(data)
	fw.Close()

	frame := ws.NewFrame(ws.OpText, true, buf.Bytes())
	frame.Header.Rsv = ws.Rsv(true, false, false) // Set compression bit
	return ws.WriteFrame(w.conn, frame)
}

type TelemetryMessage struct {
	Handler string          `json:"handler"`
	Data    json.RawMessage `json:"data"`
}

type CANFrame struct {
	ID    uint32 `json:"id"`
	Value []byte `json:"value"`
}

type CANData struct {
	Frames []CANFrame `json:"frames"`
}

func sendTelemetry(conn *wsConn, vin string) error {
	// Generate fake CAN frames
	frames := generateFakeCANFrames()
	
	canData := CANData{Frames: frames}
	canDataBytes, _ := json.Marshal(canData)

	msg := TelemetryMessage{
		Handler: "can",
		Data:    canDataBytes,
	}

	msgBytes, err := json.Marshal(msg)
	if err != nil {
		return err
	}

	logger.Debug().Str("vin", vin).Int("frames", len(frames)).Msg("Sending telemetry")
	return conn.Write(msgBytes)
}

func generateFakeCANFrames() []CANFrame {
	// Generate 10-50 random CAN frames
	numFrames := rand.Intn(40) + 10
	frames := make([]CANFrame, numFrames)

	// Common CAN IDs for vehicle telemetry
	canIDs := []uint32{
		0x100, // Speed
		0x101, // RPM
		0x102, // Battery SOC
		0x103, // Battery voltage
		0x104, // Temperature
		0x105, // GPS lat
		0x106, // GPS lon
		0x200, // Door status
		0x201, // Light status
		0x300, // HVAC
	}

	for i := 0; i < numFrames; i++ {
		frames[i] = CANFrame{
			ID:    canIDs[rand.Intn(len(canIDs))],
			Value: make([]byte, 8),
		}
		rand.Read(frames[i].Value)
	}

	return frames
}

func generateVIN(prefix string) string {
	// Pad prefix to 8 chars
	for len(prefix) < 8 {
		prefix += "X"
	}
	if len(prefix) > 8 {
		prefix = prefix[:8]
	}

	// Generate remaining 9 characters (position 9 is check digit, calculated later)
	chars := "ABCDEFGHJKLMNPRSTUVWXYZ0123456789"
	suffix := make([]byte, 8)
	for i := range suffix {
		suffix[i] = chars[rand.Intn(len(chars))]
	}

	vin := prefix + "X" + string(suffix) // X is placeholder for check digit
	
	// Calculate check digit
	checkDigit := calculateCheckDigit(vin)
	vin = vin[:8] + string(checkDigit) + vin[9:]

	return vin
}

func calculateCheckDigit(vin string) byte {
	weights := []int{8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2}
	values := map[byte]int{
		'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5, 'F': 6, 'G': 7, 'H': 8,
		'J': 1, 'K': 2, 'L': 3, 'M': 4, 'N': 5, 'P': 7, 'R': 9,
		'S': 2, 'T': 3, 'U': 4, 'V': 5, 'W': 6, 'X': 7, 'Y': 8, 'Z': 9,
		'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9,
	}

	sum := 0
	for i, c := range vin {
		if v, ok := values[byte(c)]; ok {
			sum += v * weights[i]
		}
	}

	remainder := sum % 11
	if remainder == 10 {
		return 'X'
	}
	return byte('0' + remainder)
}
