package web

import (
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"gitea.dooplex.hu/admin/felhom-controller/internal/backup"
	"gitea.dooplex.hu/admin/felhom-controller/internal/config"
	"gitea.dooplex.hu/admin/felhom-controller/internal/scheduler"
	"gitea.dooplex.hu/admin/felhom-controller/internal/stacks"
	"gitea.dooplex.hu/admin/felhom-controller/internal/system"
)

type Server struct {
	cfg          *config.Config
	stackMgr     *stacks.Manager
	cpuCollector *system.CPUCollector
	backupMgr    *backup.Manager
	scheduler    *scheduler.Scheduler
	logger       *log.Logger
	version      string
	tmpl         *template.Template

	sessions   map[string]*session
	sessionsMu sync.RWMutex
}

func NewServer(cfg *config.Config, stackMgr *stacks.Manager, cpuCollector *system.CPUCollector, backupMgr *backup.Manager, sched *scheduler.Scheduler, logger *log.Logger, version string) *Server {
	s := &Server{
		cfg:          cfg,
		stackMgr:     stackMgr,
		cpuCollector: cpuCollector,
		backupMgr:    backupMgr,
		scheduler:    sched,
		logger:       logger,
		version:      version,
		sessions:     make(map[string]*session),
	}
	s.loadTemplates()
	go s.cleanupSessions()
	return s
}

func (s *Server) loadTemplates() {
	s.tmpl = template.Must(
		template.New("").Funcs(s.templateFuncMap()).ParseFS(templateFS, "templates/*.html"),
	)
}

// ServeHTTP handles all non-API web requests.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path

	switch {
	case path == "/" || path == "/dashboard":
		s.dashboardHandler(w, r)
	case path == "/stacks":
		s.stacksHandler(w, r)
	case path == "/backups":
		s.backupsHandler(w, r)
	case path == "/monitoring":
		s.monitoringHandler(w, r)
	case strings.HasPrefix(path, "/stacks/") && strings.HasSuffix(path, "/logs"):
		name := strings.TrimPrefix(path, "/stacks/")
		name = strings.TrimSuffix(name, "/logs")
		s.logsHandler(w, r, name)
	case strings.HasPrefix(path, "/stacks/") && strings.HasSuffix(path, "/deploy"):
		name := strings.TrimPrefix(path, "/stacks/")
		name = strings.TrimSuffix(name, "/deploy")
		s.deployHandler(w, r, name)
	case path == "/static/style.css":
		s.serveCSSHandler(w, r)
	case path == "/static/chart.min.js":
		s.serveChartJSHandler(w, r)
	case path == "/static/felhom-logo.svg":
		s.serveLogoHandler(w, r)
	case strings.HasPrefix(path, "/static/assets/"):
		s.serveAsset(w, r, strings.TrimPrefix(path, "/static/assets/"))
	case strings.HasPrefix(path, "/apps/"):
		slug := strings.TrimPrefix(path, "/apps/")
		s.appDetailHandler(w, r, slug)
	default:
		http.NotFound(w, r)
	}
}

func (s *Server) render(w http.ResponseWriter, name string, data interface{}) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	if err := s.tmpl.ExecuteTemplate(w, name, data); err != nil {
		s.logger.Printf("[ERROR] Template error (%s): %v", name, err)
		http.Error(w, "Internal error", http.StatusInternalServerError)
	}
}

// --- Static file / asset serving ---

func (s *Server) serveCSSHandler(w http.ResponseWriter, r *http.Request) {
	data, err := templateFS.ReadFile("templates/style.css")
	if err != nil {
		http.Error(w, "CSS not found", 500)
		return
	}
	w.Header().Set("Content-Type", "text/css; charset=utf-8")
	w.Header().Set("Cache-Control", "public, max-age=3600")
	w.Write(data)
}

func (s *Server) serveChartJSHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
	w.Header().Set("Cache-Control", "public, max-age=86400")
	w.Write(chartJS)
}

func (s *Server) serveLogoHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "image/svg+xml")
	w.Header().Set("Cache-Control", "public, max-age=86400")
	fmt.Fprint(w, felhomLogoSVG)
}

// serveAsset serves baked-in app assets (logos, screenshots) from /usr/share/felhom/assets/
const assetsDir = "/usr/share/felhom/assets"

func (s *Server) serveAsset(w http.ResponseWriter, r *http.Request, filename string) {
	filename = filepath.Base(filename)
	path := filepath.Join(assetsDir, filename)

	if _, err := os.Stat(path); os.IsNotExist(err) {
		http.NotFound(w, r)
		return
	}

	w.Header().Set("Cache-Control", "public, max-age=86400")
	http.ServeFile(w, r, path)
}
