package homekit

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/AlexxIT/go2rtc/internal/api"
	"github.com/AlexxIT/go2rtc/internal/app"
	"github.com/AlexxIT/go2rtc/internal/streams"
	"github.com/AlexxIT/go2rtc/pkg/hap"
	"github.com/AlexxIT/go2rtc/pkg/mdns"
)

func apiHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		sources, err := discovery()
		if err != nil {
			api.Error(w, err)
			return
		}

		urls := findHomeKitURLs()
		for id, u := range urls {
			deviceID := u.Query().Get("device_id")
			for _, source := range sources {
				if strings.Contains(source.URL, deviceID) {
					source.Location = id
					break
				}
			}
		}

		for _, source := range sources {
			if source.Location == "" {
				source.Location = " "
			}
		}

		api.ResponseSources(w, sources)

	case "POST":
		if err := r.ParseMultipartForm(1024); err != nil {
			api.Error(w, err)
			return
		}

		if err := apiPair(r.Form.Get("id"), r.Form.Get("url")); err != nil {
			api.Error(w, err)
		}

	case "DELETE":
		if err := r.ParseMultipartForm(1024); err != nil {
			api.Error(w, err)
			return
		}

		if err := apiUnpair(r.Form.Get("id")); err != nil {
			api.Error(w, err)
		}
	}
}

func discovery() ([]*api.Source, error) {
	var sources []*api.Source

	// 1. Get streams from Discovery
	err := mdns.Discovery(mdns.ServiceHAP, func(entry *mdns.ServiceEntry) bool {
		log.Trace().Msgf("[homekit] mdns=%s", entry)

		category := entry.Info[hap.TXTCategory]
		if entry.Complete() && (category == hap.CategoryCamera || category == hap.CategoryDoorbell) {
			source := &api.Source{
				Name: entry.Name,
				Info: entry.Info[hap.TXTModel],
				URL: fmt.Sprintf(
					"homekit://%s:%d?device_id=%s&feature=%s&status=%s",
					entry.IP, entry.Port, entry.Info[hap.TXTDeviceID],
					entry.Info[hap.TXTFeatureFlags], entry.Info[hap.TXTStatusFlags],
				),
			}

			sources = append(sources, source)
		}
		return false
	})

	if err != nil {
		return nil, err
	}

	return sources, nil
}

func apiPair(id, url string) error {
	conn, err := hap.Pair(url)
	if err != nil {
		return err
	}

	streams.New(id, conn.URL())

	return app.PatchConfig([]string{"streams", id}, conn.URL())
}

func apiUnpair(id string) error {
	stream := streams.Get(id)
	if stream == nil {
		return errors.New(api.StreamNotFound)
	}

	rawURL := findHomeKitURL(stream.Sources())
	if rawURL == "" {
		return errors.New("not homekit source")
	}

	if err := hap.Unpair(rawURL); err != nil {
		return err
	}

	streams.Delete(id)

	return app.PatchConfig([]string{"streams", id}, nil)
}

func findHomeKitURLs() map[string]*url.URL {
	urls := map[string]*url.URL{}
	for name, sources := range streams.GetAllSources() {
		if rawURL := findHomeKitURL(sources); rawURL != "" {
			if u, err := url.Parse(rawURL); err == nil {
				urls[name] = u
			}
		}
	}
	return urls
}
