package web

import (
	"embed"
	"encoding/json"
	"io/fs"
	"log"
	"net/http"

	"github.com/duffy/usb-server/internal/config"
	"github.com/duffy/usb-server/internal/protocol"
	"github.com/duffy/usb-server/internal/token"
)

//go:embed static
var staticFiles embed.FS

// Handler provides the web UI and API
type Handler struct {
	cfg     *config.Config
	cfgPath string
	mux     *http.ServeMux

	// Callbacks for device operations
	GetDevices        func() interface{}
	AttachDevice      func(clientID, busID string) error
	DetachDevice      func(clientID, busID string) error
	SetAutoConnect    func(vendorID, productID string, enabled bool) error
	IsAutoConnect     func(vendorID, productID string) bool
	ForceDetachDevice func(clientID, busID string) error
	InstallService    func() error
	UninstallService  func() error
	GetStatus         func() map[string]interface{}
}

// NewHandler creates a new web handler
func NewHandler(cfg *config.Config, cfgPath string) *Handler {
	h := &Handler{
		cfg:     cfg,
		cfgPath: cfgPath,
		mux:     http.NewServeMux(),
	}
	h.setupRoutes()
	return h
}

func (h *Handler) setupRoutes() {
	// Static files
	staticFS, _ := fs.Sub(staticFiles, "static")
	h.mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticFS))))
	h.mux.HandleFunc("/", h.handleIndex)

	// API endpoints
	h.mux.HandleFunc("/api/status", h.handleStatus)
	h.mux.HandleFunc("/api/devices", h.handleDevices)
	h.mux.HandleFunc("/api/attach", h.handleAttach)
	h.mux.HandleFunc("/api/detach", h.handleDetach)
	h.mux.HandleFunc("/api/config", h.handleConfig)
	h.mux.HandleFunc("/api/generate-token", h.handleGenerateToken)
	h.mux.HandleFunc("/api/apply-tokens", h.handleApplyTokens)
	h.mux.HandleFunc("/api/auto-connect", h.handleAutoConnect)
	h.mux.HandleFunc("/api/force-detach", h.handleForceDetach)
	h.mux.HandleFunc("/api/service/install", h.handleServiceInstall)
	h.mux.HandleFunc("/api/service/uninstall", h.handleServiceUninstall)
}

// ServeHTTP implements http.Handler
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.mux.ServeHTTP(w, r)
}

func (h *Handler) handleIndex(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/" {
		http.NotFound(w, r)
		return
	}
	data, err := staticFiles.ReadFile("static/index.html")
	if err != nil {
		http.Error(w, "Internal error", 500)
		return
	}
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.Write(data)
}

func (h *Handler) handleStatus(w http.ResponseWriter, r *http.Request) {
	status := map[string]interface{}{
		"connected": false,
		"mode":      h.cfg.Mode,
		"name":      h.cfg.Name,
	}
	if h.GetStatus != nil {
		status = h.GetStatus()
	}
	writeJSON(w, status)
}

func (h *Handler) handleDevices(w http.ResponseWriter, r *http.Request) {
	if h.GetDevices == nil {
		writeJSON(w, map[string]interface{}{"mode": h.cfg.Mode})
		return
	}
	writeJSON(w, h.GetDevices())
}

func (h *Handler) handleAttach(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Method not allowed", 405)
		return
	}

	var req struct {
		ClientID string `json:"client_id"`
		BusID    string `json:"bus_id"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeJSON(w, map[string]interface{}{"ok": false, "error": "invalid request"})
		return
	}

	if h.AttachDevice == nil {
		writeJSON(w, map[string]interface{}{"ok": false, "error": "not in use mode"})
		return
	}

	if err := h.AttachDevice(req.ClientID, req.BusID); err != nil {
		writeJSON(w, map[string]interface{}{"ok": false, "error": err.Error()})
		return
	}

	writeJSON(w, map[string]interface{}{"ok": true})
}

func (h *Handler) handleDetach(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Method not allowed", 405)
		return
	}

	var req struct {
		ClientID string `json:"client_id"`
		BusID    string `json:"bus_id"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeJSON(w, map[string]interface{}{"ok": false, "error": "invalid request"})
		return
	}

	if h.DetachDevice == nil {
		writeJSON(w, map[string]interface{}{"ok": false, "error": "not in use mode"})
		return
	}

	if err := h.DetachDevice(req.ClientID, req.BusID); err != nil {
		writeJSON(w, map[string]interface{}{"ok": false, "error": err.Error()})
		return
	}

	writeJSON(w, map[string]interface{}{"ok": true})
}

func (h *Handler) handleConfig(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		writeJSON(w, h.cfg)
		return
	}

	if r.Method == "POST" {
		var updates struct {
			RelayAddr        string `json:"relay_addr"`
			Mode             string `json:"mode"`
			Name             string `json:"name"`
			WebPort          int    `json:"web_port"`
			AllowForceDetach *bool  `json:"allow_force_detach,omitempty"`
			DirectPort       *int   `json:"direct_port,omitempty"`
			DisableDirect    *bool  `json:"disable_direct,omitempty"`
		}
		if err := json.NewDecoder(r.Body).Decode(&updates); err != nil {
			writeJSON(w, map[string]interface{}{"ok": false, "error": "invalid request"})
			return
		}

		if updates.RelayAddr != "" {
			h.cfg.RelayAddr = updates.RelayAddr
		}
		if protocol.ValidMode(updates.Mode) {
			h.cfg.Mode = updates.Mode
		}
		if updates.Name != "" {
			h.cfg.Name = updates.Name
		}
		if updates.WebPort > 0 {
			h.cfg.WebPort = updates.WebPort
		}
		if updates.AllowForceDetach != nil {
			h.cfg.AllowForceDetach = *updates.AllowForceDetach
		}
		if updates.DirectPort != nil && *updates.DirectPort >= 0 && *updates.DirectPort <= 65535 {
			h.cfg.DirectPort = *updates.DirectPort
		}
		if updates.DisableDirect != nil {
			h.cfg.DisableDirect = *updates.DisableDirect
		}

		if err := h.cfg.Save(h.cfgPath); err != nil {
			writeJSON(w, map[string]interface{}{"ok": false, "error": err.Error()})
			return
		}

		writeJSON(w, map[string]interface{}{"ok": true})
		return
	}

	http.Error(w, "Method not allowed", 405)
}

func (h *Handler) handleGenerateToken(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Method not allowed", 405)
		return
	}

	tokens, err := token.Generate()
	if err != nil {
		writeJSON(w, map[string]interface{}{"error": err.Error()})
		return
	}

	hash := tokens.Hash()

	// Save tokens to config
	h.cfg.Token1 = tokens.Token1
	h.cfg.Token2 = tokens.Token2
	h.cfg.Token3 = tokens.Token3
	h.cfg.Hash = hash
	h.cfg.Save(h.cfgPath)

	writeJSON(w, map[string]interface{}{
		"token1": tokens.Token1,
		"token2": tokens.Token2,
		"token3": tokens.Token3,
		"hash":   hash,
	})
}

func (h *Handler) handleApplyTokens(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Method not allowed", 405)
		return
	}

	var req struct {
		Token1 string `json:"token1"`
		Token2 string `json:"token2"`
		Token3 string `json:"token3"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeJSON(w, map[string]interface{}{"ok": false, "error": "invalid request"})
		return
	}

	hash := token.HashFromTokens(req.Token1, req.Token2, req.Token3)

	h.cfg.Token1 = req.Token1
	h.cfg.Token2 = req.Token2
	h.cfg.Token3 = req.Token3
	h.cfg.Hash = hash
	h.cfg.Save(h.cfgPath)

	writeJSON(w, map[string]interface{}{"ok": true, "hash": hash})
}

func (h *Handler) handleAutoConnect(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Method not allowed", 405)
		return
	}

	var req struct {
		VendorID  string `json:"vendor_id"`
		ProductID string `json:"product_id"`
		Enabled   bool   `json:"enabled"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeJSON(w, map[string]interface{}{"ok": false, "error": "invalid request"})
		return
	}

	if h.SetAutoConnect == nil {
		writeJSON(w, map[string]interface{}{"ok": false, "error": "not in use mode"})
		return
	}

	if err := h.SetAutoConnect(req.VendorID, req.ProductID, req.Enabled); err != nil {
		writeJSON(w, map[string]interface{}{"ok": false, "error": err.Error()})
		return
	}

	writeJSON(w, map[string]interface{}{"ok": true})
}

func (h *Handler) handleForceDetach(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Method not allowed", 405)
		return
	}

	var req struct {
		ClientID string `json:"client_id"`
		BusID    string `json:"bus_id"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeJSON(w, map[string]interface{}{"ok": false, "error": "invalid request"})
		return
	}

	if h.ForceDetachDevice == nil {
		writeJSON(w, map[string]interface{}{"ok": false, "error": "not in use mode"})
		return
	}

	if err := h.ForceDetachDevice(req.ClientID, req.BusID); err != nil {
		writeJSON(w, map[string]interface{}{"ok": false, "error": err.Error()})
		return
	}

	writeJSON(w, map[string]interface{}{"ok": true})
}

func (h *Handler) handleServiceInstall(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Method not allowed", 405)
		return
	}
	if h.InstallService == nil {
		writeJSON(w, map[string]interface{}{"error": "service management not available"})
		return
	}
	if err := h.InstallService(); err != nil {
		writeJSON(w, map[string]interface{}{"error": err.Error()})
		return
	}
	writeJSON(w, map[string]interface{}{"message": "Service installiert und gestartet"})
}

func (h *Handler) handleServiceUninstall(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Method not allowed", 405)
		return
	}
	if h.UninstallService == nil {
		writeJSON(w, map[string]interface{}{"error": "service management not available"})
		return
	}
	if err := h.UninstallService(); err != nil {
		writeJSON(w, map[string]interface{}{"error": err.Error()})
		return
	}
	writeJSON(w, map[string]interface{}{"message": "Service deinstalliert"})
}

func writeJSON(w http.ResponseWriter, v interface{}) {
	w.Header().Set("Content-Type", "application/json")
	if err := json.NewEncoder(w).Encode(v); err != nil {
		log.Printf("[web] JSON encode error: %v", err)
	}
}
