package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"3588AdminBackend/internal/api"
	"3588AdminBackend/internal/config"
	"3588AdminBackend/internal/service"
	"3588AdminBackend/internal/storage"
	"3588AdminBackend/internal/web"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
	"github.com/go-chi/cors"
)

func main() {
	cfgPath := "managerd.json"
	if len(os.Args) > 1 {
		cfgPath = os.Args[1]
	}

	cfg, err := config.LoadConfig(cfgPath)
	if err != nil {
		log.Fatalf("failed to load config: %v", err)
	}

	// Initialize Services
	agentClient := service.NewAgentClient(cfg)
	store, err := storage.OpenSQLite(cfg.DBPathOrDefault())
	if err != nil {
		log.Fatalf("failed to open storage: %v", err)
	}
	regSvc := service.NewRegistryService(cfg, agentClient, storage.NewDevicesRepo(store.DB()))
	discoSvc := service.NewDiscoveryService(cfg, regSvc)
	regSvc.SetDiscovery(discoSvc)
	defer store.Close()
	taskRepo := storage.NewTasksRepo(store.DB())
	assetsRepo := storage.NewAssetsRepo(store.DB())
	modelsRepo := storage.NewModelsRepo(store.DB())
	if imported, err := service.ImportStandardTemplatesFromDir(assetsRepo, filepath.Join("templates", "standard_templates")); err != nil {
		log.Fatalf("import standard templates: %v", err)
	} else if imported > 0 {
		log.Printf("imported %d standard templates", imported)
	}
	standardModelsDir := filepath.Join("models", "standard_models")
	modelSvc := service.NewModelManagementService(modelsRepo)
	if err := modelSvc.SyncStandardModelsFromDirectory(standardModelsDir); err != nil {
		log.Fatalf("sync standard models: %v", err)
	}
	resourcesRepo := storage.NewResourcesRepo(store.DB())
	resourceSvc := service.NewResourceManagementService(resourcesRepo)
	standardResourcesDir := filepath.Join("resources", "standard_resources")
	if err := resourceSvc.SyncStandardResourcesFromDirectory(standardResourcesDir); err != nil {
		log.Printf("sync standard resources: %v", err)
	}
	stateRepo := storage.NewDeviceConfigStateRepo(store.DB())
	auditRepo := storage.NewAuditLogsRepo(store.DB())
	taskSvc := service.NewTaskService(cfg, agentClient, regSvc, taskRepo)
	taskSvc.SetStandardModels(modelsRepo, standardModelsDir)
	taskSvc.SetStandardResources(resourcesRepo)
	taskSvc.SetDeviceConfigStateRepo(stateRepo)
	taskSvc.SetAuditLogRepo(auditRepo)
	if err := taskSvc.LoadPersistedTasks(); err != nil {
		log.Printf("load persisted tasks: %v", err)
	}
	tplSvc := service.NewTemplateService(cfg)
	h := api.NewHandler(discoSvc, regSvc, agentClient, taskSvc, tplSvc)

	r := chi.NewRouter()
	r.Use(middleware.Logger)
	r.Use(middleware.Recoverer)
	r.Use(cors.Handler(cors.Options{
		AllowedOrigins: []string{"http://localhost:5173", "http://127.0.0.1:5173"},
		AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
		AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-RK-Token", "X-Model-Sha256"},
		MaxAge:         300,
	}))

	r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("ok"))
	})
	r.Get("/openapi.json", api.OpenAPI)
	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "/ui", http.StatusFound)
	})

	ui, err := web.NewUI(discoSvc, regSvc, agentClient, taskSvc, tplSvc, service.NewConfigPreviewService(cfg, assetsRepo))
	if err != nil {
		log.Fatalf("failed to init ui: %v", err)
	}
	ui.SetStateRepo(stateRepo)
	ui.SetAuditRepo(auditRepo)
	ui.SetDBPath(cfg.DBPathOrDefault())
	ui.SetResourcesRepo(resourcesRepo)
	uiRouter, err := ui.Routes()
	if err != nil {
		log.Fatalf("failed to init ui routes: %v", err)
	}
	r.Mount("/ui", http.StripPrefix("/ui", uiRouter))

	// API Routes
	r.Route("/api", func(r chi.Router) {
		r.Post("/discovery/search", h.Search)
		r.Get("/devices", h.ListDevices)
		r.Post("/devices", h.CreateDevice)
		r.Get("/devices/{id}", h.GetDevice)

		// Proxy routes for device actions
		r.Get("/devices/{id}/info", h.ProxyAgent)
		r.Get("/devices/{id}/config/status", h.ProxyAgent)
		r.Put("/devices/{id}/config/candidate", h.ProxyAgent)
		r.Post("/devices/{id}/config/candidate/apply", h.ProxyAgent)
		r.Post("/devices/{id}/reload", h.ProxyAgent)
		r.Post("/devices/{id}/rollback", h.ProxyAgent)
		r.Get("/devices/{id}/graphs", h.ProxyAgent)
		r.Get("/devices/{id}/graphs/{name}", h.ProxyAgent)
		r.Get("/devices/{id}/logs", h.ProxyAgent)
		r.Post("/devices/{id}/config/apply", h.ProxyAgent)
		r.Get("/devices/{id}/models", h.ProxyAgent)
		r.Post("/devices/{id}/media-server/start", h.ProxyAgent)
		r.Post("/devices/{id}/media-server/restart", h.ProxyAgent)
		r.Post("/devices/{id}/media-server/stop", h.ProxyAgent)
		r.Get("/devices/{id}/media-server/status", h.ProxyAgent)

		// Generic passthrough for device agent /v1/* (covers config/ui/*, face-gallery, etc.)
		r.Handle("/devices/{id}/v1/*", http.HandlerFunc(h.ProxyAgentV1))

		// Task routes
		r.Post("/tasks", h.CreateTask)
		r.Get("/tasks", h.ListTasks)
		r.Get("/tasks/{id}/events", h.TaskEvents)

		// Template routes
		r.Get("/templates", h.ListTemplates)
		r.Get("/templates/{name}", h.GetTemplate)

		// Model routes
		r.Post("/devices/{id}/models/upload", h.UploadModel)
	})

	fmt.Printf("Starting managerd on %s\n", cfg.Listen)
	if err := http.ListenAndServe(cfg.Listen, r); err != nil {
		log.Fatalf("failed to start server: %v", err)
	}
}
