package desktop

import (
	"fmt"
	"io/fs"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"strings"
	"syscall"

	"github.com/muyue/muyue/internal/api"
	"github.com/muyue/muyue/internal/config"
	"github.com/muyue/muyue/internal/version"
	"github.com/muyue/muyue/web"
)

type options struct {
	port   int
	noOpen bool
}

type option func(*options)

func withPort(port int) option {
	return func(o *options) { o.port = port }
}

func withNoOpen(noOpen bool) option {
	return func(o *options) { o.noOpen = noOpen }
}

func parseFlags(args []string) []option {
	var opts []option
	for _, arg := range args {
		switch {
		case arg == "--no-open":
			opts = append(opts, withNoOpen(true))
		case strings.HasPrefix(arg, "--port="):
			if p, err := strconv.Atoi(strings.TrimPrefix(arg, "--port=")); err == nil {
				opts = append(opts, withPort(p))
			}
		case arg == "--port":
			// handled as prefix case
		}
	}
	return opts
}

func Run(cfg *config.MuyueConfig, args []string) error {
	o := options{}
	for _, opt := range parseFlags(args) {
		opt(&o)
	}

	log.Printf("%s Desktop v%s", version.Name, version.Version)

	srv := api.NewServer(cfg)

	frontendFS, err := fs.Sub(web.Assets, "dist")
	if err != nil {
		return fmt.Errorf("frontend assets: %w", err)
	}

	mux := http.NewServeMux()
	mux.Handle("/api/", srv)
	mux.Handle("/", spaHandler(http.FileServer(http.FS(frontendFS))))

	addr := fmt.Sprintf("127.0.0.1:%d", o.port)
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return fmt.Errorf("bind %s: %w", addr, err)
	}
	port := listener.Addr().(*net.TCPAddr).Port

	go func() {
		if err := http.Serve(listener, mux); err != nil {
			log.Fatalf("Server error: %v", err)
		}
	}()

	url := fmt.Sprintf("http://127.0.0.1:%d", port)
	log.Printf("Listening on %s", url)

	if !o.noOpen {
		openBrowser(url)
		log.Printf("Opened browser")
	}

	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit

	log.Println("Shutting down...")
	return nil
}

func spaHandler(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		path := r.URL.Path
		if path != "/" && !strings.Contains(path, ".") {
			r.URL.Path = "/"
		}
		next.ServeHTTP(w, r)
	})
}

func openBrowser(url string) {
	var cmd *exec.Cmd
	switch {
	case exists("xdg-open"):
		cmd = exec.Command("xdg-open", url)
	case exists("open"):
		cmd = exec.Command("open", url)
	case exists("cmd"):
		cmd = exec.Command("cmd", "/c", "start", url)
	default:
		fmt.Printf("Open manually: %s\n", url)
		return
	}
	_ = cmd.Start()
}

func exists(name string) bool {
	_, err := exec.LookPath(name)
	return err == nil
}
