package main

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"net/http"
	"strconv"
)

const apiKey = "4e75b8927940adc29e2e1eac042bf92bcddd57fe"
const netboxBaseURL = "https://netbox.internal.faercol.me/api/"

type Device struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

type Object struct {
	Device Device `json:"device"`
}

type CableTermination struct {
	Object Object `json:"object"`
}

type Cable struct {
	ID            int                `json:"id"`
	ATerminations []CableTermination `json:"a_terminations"`
	BTerminations []CableTermination `json:"b_terminations"`
}

type VM struct {
	ID     int    `json:"id"`
	Name   string `json:"name"`
	Device Device `json:"device"`
}

type vmsResponse struct {
	Results []VM `json:"results"`
}

type deviceResponse struct {
	Results []Device `json:"results"`
}

type cableResponse struct {
	Results []Cable `json:"results"`
}

type ElementData struct {
	ID     string `json:"id"`
	Source string `json:"source,omitempty"`
	Target string `json:"target,omitempty"`
	Parent string `json:"parent,omitempty"`
}

type Element struct {
	Data    ElementData `json:"data"`
	Classes []string    `json:"classes"`
}

func GetDevices() ([]Device, error) {
	query, err := http.NewRequest("GET", netboxBaseURL+"dcim/devices", nil)
	if err != nil {
		return nil, err
	}

	query.Header.Set("Authorization", "Token "+apiKey)

	resp, err := http.DefaultClient.Do(query)
	if err != nil {
		return nil, err
	}

	var res deviceResponse
	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if err := json.Unmarshal(respBody, &res); err != nil {
		return nil, err
	}

	return res.Results, nil
}

func GetCables() ([]Cable, error) {
	query, err := http.NewRequest("GET", netboxBaseURL+"dcim/cables", nil)
	if err != nil {
		return nil, err
	}

	query.Header.Set("Authorization", "Token "+apiKey)

	resp, err := http.DefaultClient.Do(query)
	if err != nil {
		return nil, err
	}

	var res cableResponse
	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if err := json.Unmarshal(respBody, &res); err != nil {
		return nil, err
	}

	return res.Results, nil
}

func GetVMs() ([]VM, error) {
	query, err := http.NewRequest("GET", netboxBaseURL+"virtualization/virtual-machines", nil)
	if err != nil {
		return nil, err
	}

	query.Header.Set("Authorization", "Token "+apiKey)

	resp, err := http.DefaultClient.Do(query)
	if err != nil {
		return nil, err
	}

	var res vmsResponse
	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if err := json.Unmarshal(respBody, &res); err != nil {
		return nil, err
	}

	return res.Results, nil
}

func main() {
	srv := http.NewServeMux()

	srv.HandleFunc("/static/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("Serving static file")
		fs := http.FileServer(http.Dir("./static"))
		http.StripPrefix("/static", fs).ServeHTTP(w, r)
	})
	srv.HandleFunc("/api/data", func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("Serving API route")
		devices, err := GetDevices()
		if err != nil {
			fmt.Printf("Failed to get devices: %s\n", err)
			w.WriteHeader(500)
			return
		}

		cables, err := GetCables()
		if err != nil {
			fmt.Printf("Failed to get cables: %s\n", err)
			w.WriteHeader(500)
			return
		}

		vms, err := GetVMs()
		if err != nil {
			fmt.Printf("Failed to get VMs: %s\n", err)
			w.WriteHeader(500)
			return
		}

		resp := []Element{}
		for _, d := range devices {
			resp = append(resp, Element{Data: ElementData{ID: d.Name}})
		}
		for _, c := range cables {
			resp = append(resp, Element{Data: ElementData{ID: "link-" + strconv.FormatInt(int64(c.ID), 10), Source: c.ATerminations[0].Object.Device.Name, Target: c.BTerminations[0].Object.Device.Name}})
		}
		for _, v := range vms {
			resp = append(resp, Element{Data: ElementData{ID: v.Name}}, Element{Data: ElementData{ID: "vm-" + strconv.FormatInt(int64(v.ID), 10), Source: v.Name, Target: v.Device.Name}, Classes: []string{"edge", "virtual-edge"}})
		}

		respBody, err := json.Marshal(resp)
		if err != nil {
			fmt.Printf("Failed to serialize data: %s\n", err)
			w.WriteHeader(500)
			return
		}

		w.Write(respBody)
	})
	srv.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("Serving main root")
		tpl, err := template.New("index.html").ParseFiles("templates/index.html")
		if err != nil {
			fmt.Printf("Failed to read template: %s\n", err)
			w.WriteHeader(500)
			return
		}

		if err := tpl.Execute(w, nil); err != nil {
			fmt.Printf("Failed to execute template: %s\n", err)
			w.WriteHeader(500)
			return
		}
	})

	if err := http.ListenAndServe("127.0.0.1:5000", srv); err != nil {
		panic(err)
	}
}
