package handler

import (
	"embed"
	"encoding/json"
	"html/template"
	"net/http"
	"path"

	"freeleaps.com/gitea-webhook-ambassador/internal/handler"
	"freeleaps.com/gitea-webhook-ambassador/internal/logger"
)

type DashboardHandler struct {
	templates      *template.Template
	fs             embed.FS
	projectHandler *handler.ProjectHandler
	adminHandler   *handler.AdminHandler
	logsHandler    *handler.LogsHandler
	healthHandler  *handler.HealthHandler
}

func NewDashboardHandler(fs embed.FS, projectHandler *handler.ProjectHandler, adminHandler *handler.AdminHandler, logsHandler *handler.LogsHandler, healthHandler *handler.HealthHandler) (*DashboardHandler, error) {
	templates, err := template.ParseFS(fs, "templates/*.html")
	if err != nil {
		return nil, err
	}

	return &DashboardHandler{
		templates:      templates,
		fs:             fs,
		projectHandler: projectHandler,
		adminHandler:   adminHandler,
		logsHandler:    logsHandler,
		healthHandler:  healthHandler,
	}, nil
}

func (h *DashboardHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	switch r.URL.Path {
	case "/login":
		h.handleLogin(w, r)
	case "/dashboard":
		h.handleDashboard(w, r)
	case "/api/projects":
		h.handleProjects(w, r)
	case "/api/keys":
		h.handleAPIKeys(w, r)
	case "/api/logs":
		h.handleLogs(w, r)
	case "/api/health":
		h.handleHealth(w, r)
	default:
		// Serve static files
		if path.Ext(r.URL.Path) != "" {
			h.serveStaticFile(w, r)
			return
		}
		http.NotFound(w, r)
	}
}

func (h *DashboardHandler) handleLogin(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}
	logger.Debug("Serving login page")
	h.templates.ExecuteTemplate(w, "login.html", nil)
}

func (h *DashboardHandler) handleDashboard(w http.ResponseWriter, r *http.Request) {
	h.templates.ExecuteTemplate(w, "dashboard.html", nil)
}

func (h *DashboardHandler) handleProjects(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case http.MethodGet:
		h.projectHandler.HandleGetProjectMapping(w, r)
	case http.MethodPost:
		h.projectHandler.HandleCreateProjectMapping(w, r)
	default:
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
	}
}

func (h *DashboardHandler) handleAPIKeys(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case http.MethodGet:
		h.adminHandler.HandleListAPIKeys(w, r)
	case http.MethodPost:
		h.adminHandler.HandleCreateAPIKey(w, r)
	case http.MethodDelete:
		h.adminHandler.HandleDeleteAPIKey(w, r)
	default:
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
	}
}

func (h *DashboardHandler) handleLogs(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}
	h.logsHandler.HandleGetTriggerLogs(w, r)
}

func (h *DashboardHandler) handleHealth(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// Capture the health handler response
	recorder := newResponseRecorder(w)
	h.healthHandler.HandleHealth(recorder, r)

	// If it's not JSON or there was an error, just copy the response
	if recorder.Header().Get("Content-Type") != "application/json" {
		recorder.copyToResponseWriter(w)
		return
	}

	// Parse the health check response and format it for the dashboard
	var healthData map[string]interface{}
	if err := json.Unmarshal(recorder.Body(), &healthData); err != nil {
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return
	}

	// Format the response for the dashboard
	response := map[string]string{
		"status": "healthy",
	}
	if healthData["status"] != "ok" {
		response["status"] = "unhealthy"
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

func (h *DashboardHandler) serveStaticFile(w http.ResponseWriter, r *http.Request) {
	// Remove leading slash and join with assets directory
	filePath := path.Join("assets", r.URL.Path)
	data, err := h.fs.ReadFile(filePath)
	if err != nil {
		http.NotFound(w, r)
		return
	}

	// Set MIME type based on file extension
	ext := path.Ext(r.URL.Path)
	switch ext {
	case ".css":
		w.Header().Set("Content-Type", "text/css; charset=utf-8")
	case ".js":
		w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
	case ".png":
		w.Header().Set("Content-Type", "image/png")
	case ".jpg", ".jpeg":
		w.Header().Set("Content-Type", "image/jpeg")
	default:
		w.Header().Set("Content-Type", "application/octet-stream")
	}

	// Set caching headers
	w.Header().Set("Cache-Control", "public, max-age=31536000")
	w.Write(data)
}

// responseRecorder is a custom ResponseWriter that records its mutations
type responseRecorder struct {
	headers    http.Header
	body       []byte
	statusCode int
	original   http.ResponseWriter
}

func newResponseRecorder(w http.ResponseWriter) *responseRecorder {
	return &responseRecorder{
		headers:    make(http.Header),
		statusCode: http.StatusOK,
		original:   w,
	}
}

func (r *responseRecorder) Header() http.Header {
	return r.headers
}

func (r *responseRecorder) Write(body []byte) (int, error) {
	r.body = append(r.body, body...)
	return len(body), nil
}

func (r *responseRecorder) WriteHeader(statusCode int) {
	r.statusCode = statusCode
}

func (r *responseRecorder) Body() []byte {
	return r.body
}

func (r *responseRecorder) copyToResponseWriter(w http.ResponseWriter) {
	for k, v := range r.headers {
		w.Header()[k] = v
	}
	w.WriteHeader(r.statusCode)
	w.Write(r.body)
}
