package main

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"syscall"
	"time"
)

const Version = "0.2.0"

func main() {
	listen := flag.String("listen", "127.0.0.1:7474", "host:port a escuchar (ej. 10.42.0.10:7474 para mesh wg1)")
	manifestPath := flag.String("manifest", defaultManifestPath(), "ruta al manifest YAML")
	auditPath := flag.String("audit", "local_files/audit.db", "ruta al audit.db sqlite")
	selfTest := flag.Bool("self-test", false, "smoke check, sale 0 si todo OK")
	flag.Parse()

	if *selfTest {
		fmt.Println("device_agent self-test OK")
		os.Exit(0)
	}

	// Cargar manifest.
	mf, err := LoadManifest(*manifestPath)
	if err != nil {
		log.Fatalf("manifest load %s: %v", *manifestPath, err)
	}
	log.Printf("manifest loaded device_id=%s capabilities=%d", mf.DeviceID, len(mf.Capabilities))

	// Abrir audit DB.
	_ = os.MkdirAll(filepath.Dir(*auditPath), 0700)
	audit, err := OpenAudit(*auditPath)
	if err != nil {
		log.Fatalf("audit open %s: %v", *auditPath, err)
	}
	defer audit.Close()
	log.Printf("audit db opened %s", *auditPath)

	// Construir router.
	mux := http.NewServeMux()
	mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		writeJSON(w, http.StatusOK, map[string]any{
			"ok":        true,
			"device_id": mf.DeviceID,
			"version":   Version,
			"ts":        time.Now().Unix(),
		})
	})
	mux.HandleFunc("/capabilities", func(w http.ResponseWriter, r *http.Request) {
		writeJSON(w, http.StatusOK, map[string]any{
			"device_id":    mf.DeviceID,
			"capabilities": mf.Capabilities,
		})
	})
	mux.HandleFunc("/capability", capabilityHandler(mf, audit))

	srv := &http.Server{
		Addr:              *listen,
		Handler:           loggingMiddleware(mux),
		ReadHeaderTimeout: 5 * time.Second,
	}

	// Listen explicit para detectar bind issues claro.
	ln, err := net.Listen("tcp", *listen)
	if err != nil {
		log.Fatalf("listen %s: %v", *listen, err)
	}
	log.Printf("device_agent v%s listening %s (device_id=%s)", Version, *listen, mf.DeviceID)

	// Graceful shutdown.
	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer stop()

	go func() {
		if err := srv.Serve(ln); err != nil && err != http.ErrServerClosed {
			log.Fatalf("serve: %v", err)
		}
	}()

	<-ctx.Done()
	log.Println("shutdown signal received, draining...")
	shutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	_ = srv.Shutdown(shutCtx)
	log.Println("bye")
}

func defaultManifestPath() string {
	home, _ := os.UserHomeDir()
	return filepath.Join(home, ".config", "device_agent", "manifest.yaml")
}

func writeJSON(w http.ResponseWriter, status int, body any) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	_ = json.NewEncoder(w).Encode(body)
}

func writeError(w http.ResponseWriter, status int, code, msg string) {
	writeJSON(w, status, map[string]any{
		"ok":    false,
		"error": code,
		"msg":   msg,
	})
}

func loggingMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		ww := &statusWriter{ResponseWriter: w, status: 200}
		next.ServeHTTP(ww, r)
		dur := time.Since(start).Milliseconds()
		log.Printf("%s %s %d %dms from=%s", r.Method, r.URL.Path, ww.status, dur, clientIP(r))
	})
}

type statusWriter struct {
	http.ResponseWriter
	status int
}

func (s *statusWriter) WriteHeader(code int) {
	s.status = code
	s.ResponseWriter.WriteHeader(code)
}

func clientIP(r *http.Request) string {
	host, _, err := net.SplitHostPort(r.RemoteAddr)
	if err != nil {
		return r.RemoteAddr
	}
	if strings.HasPrefix(host, "::") {
		return host
	}
	return host
}
