package server

import (
	"net/http"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"

	"warpbox/lib/alerts"
	"warpbox/lib/config"
	"warpbox/lib/security"
)

const adminSessionCookie = "warpbox_admin_session"
const adminSessionMarker = "1"

func (app *App) adminLoginEnabled() bool {
	return app.config.AdminLoginEnabled(app.config.AdminPassword != "")
}

func (app *App) adminAuthMiddleware(ctx *gin.Context) {
	if !app.adminLoginEnabled() {
		ctx.Redirect(http.StatusSeeOther, "/")
		ctx.Abort()
		return
	}

	token, err := ctx.Cookie(adminSessionCookie)
	if err != nil || token != app.adminSessionToken() {
		ctx.Redirect(http.StatusSeeOther, "/admin/login")
		ctx.Abort()
		return
	}

	ctx.Next()
}

func (app *App) adminSessionToken() string {
	// A simple deterministic token derived from the admin credentials.
	// This will improve when proper user/session storage is added.
	return app.config.AdminUsername + ":" + app.config.AdminPassword
}

func (app *App) handleAdminLogin(ctx *gin.Context) {
	if !app.adminLoginEnabled() {
		ctx.Redirect(http.StatusSeeOther, "/")
		return
	}

	// Already logged in.
	if token, err := ctx.Cookie(adminSessionCookie); err == nil && token == app.adminSessionToken() {
		ctx.Redirect(http.StatusSeeOther, "/admin/dashboard")
		return
	}

	ctx.HTML(http.StatusOK, "admin/login.html", gin.H{})
}

func (app *App) handleAdminLoginPost(ctx *gin.Context) {
	if !app.adminLoginEnabled() {
		ctx.Redirect(http.StatusSeeOther, "/")
		return
	}
	ip := app.clientIP(ctx)
	guard := app.securityGuard
	if guard == nil {
		guard = security.NewGuard()
		app.securityGuard = guard
	}
	if !guard.IsAdminWhitelisted(ip) && guard.IsBanned(ip) {
		app.logActivity("auth.admin.block", "high", "Blocked admin login from banned IP", ctx, nil)
		ctx.HTML(http.StatusTooManyRequests, "admin/login.html", gin.H{
			"ErrorMessage": "Too many failed attempts. Try again later.",
		})
		return
	}

	username := strings.TrimSpace(ctx.PostForm("username"))
	password := ctx.PostForm("password")

	if username != app.config.AdminUsername || password != app.config.AdminPassword {
		if !guard.IsAdminWhitelisted(ip) {
			banned, attempts := guard.RegisterFailedLogin(ip, app.config.SecurityLoginWindowSeconds, app.config.SecurityLoginMaxAttempts, app.config.SecurityBanSeconds)
			app.logActivity("auth.admin.failed", "medium", "Failed admin login", ctx, map[string]string{"attempts": strconv.Itoa(attempts)})
			if banned {
				app.createAlert("Admin login brute-force blocked", "high", "security", "401", "auth.admin.bruteforce", "Too many failed admin logins triggered temporary ban.", map[string]string{"ip": ip, "attempts": strconv.Itoa(attempts)})
				app.logActivity("security.ban", "high", "Auto-banned IP after admin login failures", ctx, map[string]string{"attempts": strconv.Itoa(attempts)})
			}
		}
		ctx.HTML(http.StatusUnauthorized, "admin/login.html", gin.H{
			"ErrorMessage": "Invalid username or password.",
		})
		return
	}

	app.logActivity("auth.admin.success", "low", "Admin login successful", ctx, nil)
	secure := app.config.AdminCookieSecure
	maxAge := int(app.config.SessionTTLSeconds)

	ctx.SetCookie(adminSessionCookie, app.adminSessionToken(), maxAge, "/admin", "", secure, true)
	ctx.Redirect(http.StatusSeeOther, "/admin/dashboard")
}

func (app *App) handleAdminLogout(ctx *gin.Context) {
	secure := app.config.AdminCookieSecure
	ctx.SetCookie(adminSessionCookie, "", -1, "/admin", "", secure, true)
	ctx.Redirect(http.StatusSeeOther, "/admin/login")
}

func (app *App) handleAdminDashboard(ctx *gin.Context) {
	if !app.adminLoginEnabled() {
		ctx.Redirect(http.StatusSeeOther, "/")
		return
	}

	dashboardEnabled := config.AdminEnabledTrue
	if cfgVal := app.config.AdminEnabled; cfgVal == config.AdminEnabledAuto || cfgVal == config.AdminEnabledTrue {
		dashboardEnabled = cfgVal
	}

	ctx.HTML(http.StatusOK, "admin/dashboard.html", gin.H{
		"AdminUsername":    app.config.AdminUsername,
		"AdminEmail":       app.config.AdminEmail,
		"ActivePage":       "dashboard",
		"DashboardEnabled": string(dashboardEnabled),
	})
}

func (app *App) handleAdminAlerts(ctx *gin.Context) {
	if !app.adminLoginEnabled() {
		ctx.Redirect(http.StatusSeeOther, "/")
		return
	}

	alertsList := []alerts.Alert{}
	if app.alertStore != nil {
		var err error
		alertsList, err = app.alertStore.List(500)
		if err != nil {
			ctx.String(http.StatusInternalServerError, "Could not load alerts")
			return
		}
	}
	openCount := 0
	highCount := 0
	ackedCount := 0
	closedCount := 0
	for _, alert := range alertsList {
		switch string(alert.Status) {
		case "open":
			openCount++
		case "acked":
			ackedCount++
		case "closed":
			closedCount++
		}
		if alert.Severity == "high" && string(alert.Status) != "closed" {
			highCount++
		}
	}

	ctx.HTML(http.StatusOK, "admin/alerts.html", gin.H{
		"AdminUsername": app.config.AdminUsername,
		"AdminEmail":    app.config.AdminEmail,
		"ActivePage":    "alerts",
		"Alerts":        alertsList,
		"OpenCount":     strconv.Itoa(openCount),
		"HighCount":     strconv.Itoa(highCount),
		"AckCount":      strconv.Itoa(ackedCount),
		"ClosedCount":   strconv.Itoa(closedCount),
	})
}
