package ui

import (
	"bytes"
	"embed"
	"html/template"
	"io/fs"
	"log"
	"math"
	"net/http"
	"net/http/httptest"
	"path/filepath"
	"strconv"
	"strings"

	"epigas.gitea.cloud/RiskRancher/core/pkg/auth"
	"epigas.gitea.cloud/RiskRancher/core/pkg/domain"
	"epigas.gitea.cloud/RiskRancher/core/pkg/report"
)

//go:embed templates/* templates/components/* static/*
var CoreUIFS embed.FS

var (
	AppVersion = "dev"
	AppCommit  = "none"
)

var CoreTemplates *template.Template
var Pages map[string]*template.Template

// SetVersionInfo is called by main.go on startup to inject ldflags
func SetVersionInfo(version, commit string) {
	AppVersion = version
	AppCommit = commit
}

func init() {
	funcMap := template.FuncMap{"lower": strings.ToLower}
	Pages = make(map[string]*template.Template)

	var err error

	CoreTemplates, err = template.New("").Funcs(funcMap).ParseFS(CoreUIFS, "templates/*.gohtml", "templates/components/*.gohtml")
	if err != nil && !strings.Contains(err.Error(), "pattern matches no files") {
		log.Printf("Warning: Failed to parse master core templates: %v", err)
	}

	dashTmpl := template.New("").Funcs(funcMap)
	dashTmpl, err = dashTmpl.ParseFS(CoreUIFS, "templates/base.gohtml", "templates/dashboard.gohtml", "templates/components/*.gohtml")
	if err != nil {
		log.Fatalf("FATAL: Failed to parse dashboard shell. Err: %v", err)
	}
	Pages["dashboard"] = dashTmpl

	adminTmpl := template.New("").Funcs(funcMap)
	adminTmpl, err = adminTmpl.ParseFS(CoreUIFS, "templates/base.gohtml", "templates/admin.gohtml", "templates/components/*.gohtml")
	if err != nil {
		log.Fatalf("FATAL: Failed to parse admin shell. Err: %v", err)
	}
	Pages["admin"] = adminTmpl

	Pages["login"], err = template.New("").Funcs(funcMap).ParseFS(CoreUIFS, "templates/login.gohtml")
	if err != nil {
		log.Fatalf("FATAL: Failed to parse login. Err: %v", err)
	}

	Pages["register"], err = template.New("").Funcs(funcMap).ParseFS(CoreUIFS, "templates/register.gohtml")
	if err != nil {
		log.Fatalf("FATAL: Failed to parse register. Err: %v", err)
	}

	Pages["assets"], err = template.New("").Funcs(funcMap).ParseFS(CoreUIFS, "templates/base.gohtml", "templates/assets.gohtml", "templates/components/*.gohtml")
	if err != nil {
		log.Fatalf("FATAL: Failed to parse assets. Err: %v", err)
	}

	ingestTmpl := template.New("").Funcs(funcMap)
	ingestTmpl, err = ingestTmpl.ParseFS(CoreUIFS, "templates/base.gohtml", "templates/ingest.gohtml", "templates/components/*.gohtml")
	if err != nil {
		log.Fatalf("FATAL: Failed to parse ingest shell. Err: %v", err)
	}
	Pages["ingest"] = ingestTmpl

	adapterTmpl := template.New("").Funcs(funcMap)
	adapterTmpl, err = adapterTmpl.ParseFS(CoreUIFS, "templates/base.gohtml", "templates/adapter_builder.gohtml", "templates/components/*.gohtml")
	if err != nil {
		log.Fatalf("FATAL: Failed to parse adapter builder shell. Err: %v", err)
	}
	Pages["adapter_builder"] = adapterTmpl

	uploadTmpl := template.New("").Funcs(funcMap)
	uploadTmpl, err = uploadTmpl.ParseFS(CoreUIFS, "templates/base.gohtml", "templates/report_upload.gohtml", "templates/components/*.gohtml")
	if err != nil {
		log.Fatalf("FATAL: Failed to parse report upload template. Err: %v", err)
	}
	Pages["report_upload"] = uploadTmpl

	parserTmpl := template.New("").Funcs(funcMap)
	parserTmpl, err = parserTmpl.ParseFS(CoreUIFS, "templates/base.gohtml", "templates/report_parser.gohtml", "templates/components/*.gohtml")
	if err != nil {
		log.Fatalf("FATAL: Failed to parse report parser template. Err: %v", err)
	}
	Pages["report_parser"] = parserTmpl
}

func StaticHandler() http.Handler {
	staticFS, err := fs.Sub(CoreUIFS, "static")
	if err != nil {
		log.Fatal("Failed to load embedded static files:", err)
	}
	return http.StripPrefix("/static/", http.FileServer(http.FS(staticFS)))
}

type PageData struct {
	Tickets           any
	CurrentTab        string
	CurrentFilter     string
	CurrentAsset      string
	ReturnedCount     int
	CountCritical     int
	CountOverdue      int
	CountMine         int
	CurrentPage       int
	TotalPages        int
	NextPage          int
	PrevPage          int
	CountVerification int
	HasNext           bool
	HasPrev           bool
	Version           string
	Commit            string
}

func HandleDashboard(store domain.Store) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		userIDVal := r.Context().Value(auth.UserIDKey)
		if userIDVal == nil {
			http.Redirect(w, r, "/login", http.StatusSeeOther)
			return
		}

		userID := userIDVal.(int)
		user, err := store.GetUserByID(r.Context(), userID)
		if err != nil {
			http.Redirect(w, r, "/login", http.StatusSeeOther)
			return
		}

		if user.GlobalRole == "Sheriff" {
			http.Redirect(w, r, "/admin", http.StatusSeeOther)
			return
		}

		currentUserEmail := user.Email
		currentUserRole := user.GlobalRole

		tab := r.URL.Query().Get("tab")
		if tab == "" {
			tab = "holding_pen"
		}

		statusFilter := tab
		if tab == "holding_pen" {
			statusFilter = "Waiting to be Triaged"
		} else if tab == "chute" {
			statusFilter = "Assigned Out"
		} else if tab == "verification" {
			statusFilter = "Pending Verification"
		}

		filter := r.URL.Query().Get("filter")
		assetFilter := r.URL.Query().Get("asset")

		pageStr := r.URL.Query().Get("page")
		page, _ := strconv.Atoi(pageStr)
		if page < 1 {
			page = 1
		}
		limit := 50
		offset := (page - 1) * limit

		tickets, totalRecords, metrics, err := store.GetDashboardTickets(
			r.Context(), statusFilter, filter, assetFilter, currentUserEmail, currentUserRole, limit, offset,
		)

		if err != nil {
			http.Error(w, "Database query error: "+err.Error(), http.StatusInternalServerError)
			return
		}

		totalPages := int(math.Ceil(float64(totalRecords) / float64(limit)))
		if totalPages == 0 {
			totalPages = 1
		}

		data := PageData{
			Tickets:           tickets,
			CurrentTab:        tab,
			CurrentFilter:     filter,
			CurrentAsset:      assetFilter,
			ReturnedCount:     metrics["returned"],
			CountCritical:     metrics["critical"],
			CountOverdue:      metrics["overdue"],
			CountMine:         metrics["mine"],
			CountVerification: metrics["verification"],
			CurrentPage:       page,
			TotalPages:        totalPages,
			NextPage:          page + 1,
			PrevPage:          page - 1,
			HasNext:           page < totalPages,
			HasPrev:           page > 1,
			Version:           AppVersion,
			Commit:            AppCommit,
		}

		var buf bytes.Buffer
		if err := Pages["dashboard"].ExecuteTemplate(&buf, "base", data); err != nil {
			http.Error(w, "Template rendering error: "+err.Error(), http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		buf.WriteTo(w)
	}
}

func HandleLoginUI() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		if err := Pages["login"].ExecuteTemplate(w, "login", nil); err != nil {
			http.Error(w, "Template render error: "+err.Error(), http.StatusInternalServerError)
		}
	}
}

func HandleRegisterUI() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		if err := Pages["register"].ExecuteTemplate(w, "register", nil); err != nil {
			http.Error(w, "Template render error: "+err.Error(), http.StatusInternalServerError)
		}
	}
}

func HandleAdminDashboard(store domain.Store) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		users, _ := store.GetAllUsers(r.Context())
		config, _ := store.GetAppConfig(r.Context())
		slas, _ := store.GetSLAPolicies(r.Context())
		adapters, _ := store.GetAdapters(r.Context())

		analytics, _ := store.GetSheriffAnalytics(r.Context())
		activityFeed, _ := store.GetGlobalActivityFeed(r.Context(), 15)
		syncLogs, _ := store.GetRecentSyncLogs(r.Context(), 10)

		data := map[string]any{
			"Users":     users,
			"Config":    config,
			"SLAs":      slas,
			"Adapters":  adapters,
			"Analytics": analytics,
			"Feed":      activityFeed,
			"SyncLogs":  syncLogs,
			"Version":   AppVersion,
			"Commit":    AppCommit,
		}

		var buf bytes.Buffer
		if err := Pages["admin"].ExecuteTemplate(&buf, "base", data); err != nil {
			http.Error(w, "Template render error: "+err.Error(), http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		buf.WriteTo(w)
	}
}

func HandleIngestUI(store domain.Store) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")

		adapters, _ := store.GetAdapters(r.Context())
		data := map[string]any{
			"Adapters": adapters,
			"Version":  AppVersion,
			"Commit":   AppCommit,
		}

		if err := Pages["ingest"].ExecuteTemplate(w, "base", data); err != nil {
			http.Error(w, "Template render error: "+err.Error(), http.StatusInternalServerError)
		}
	}
}

func HandleAdapterBuilderUI(store domain.Store) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		data := map[string]any{
			"Filename": r.URL.Query().Get("filename"),
			"Version":  AppVersion,
			"Commit":   AppCommit,
		}
		if err := Pages["adapter_builder"].ExecuteTemplate(w, "base", data); err != nil {
			http.Error(w, "Template render error: "+err.Error(), http.StatusInternalServerError)
		}
	}
}

func HandleParserUI(store domain.Store) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		reportID := r.PathValue("id")

		filePath := filepath.Join(report.UploadDir, reportID)
		recorder := httptest.NewRecorder()
		report.ServeDOCXAsHTML(recorder, filePath)
		safeHTML := template.HTML(recorder.Body.String())

		data := map[string]any{
			"ReportID":     reportID,
			"RenderedHTML": safeHTML,
			"Version":      AppVersion,
			"Commit":       AppCommit,
		}

		if err := Pages["report_parser"].ExecuteTemplate(w, "base", data); err != nil {
			http.Error(w, "Template render error: "+err.Error(), http.StatusInternalServerError)
		}
	}
}

func HandlePentestUploadUI(store domain.Store) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		data := map[string]any{
			"Version": AppVersion,
			"Commit":  AppCommit,
		}
		if err := Pages["report_upload"].ExecuteTemplate(w, "base", data); err != nil {
			http.Error(w, "Template render error: "+err.Error(), http.StatusInternalServerError)
		}
	}
}
