package reseed

import (
	"context"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/hex"
	"fmt"
	"html"
	"log"
	"strings"
	"time"

	"boostai-backend/internal/config"
	"boostai-backend/internal/database"
	"boostai-backend/internal/seeddata"

	"github.com/gofiber/fiber/v2"
)

const reseedCookieName = "boostai_reseed_auth"

type runFunc func(ctx context.Context, mockDataDir string) (seeddata.Summary, error)

type Handler struct {
	cfg    *config.Config
	runner runFunc
}

type pageData struct {
	Authorized  bool
	Environment string
	MockDataDir string
	Error       string
	Success     string
	Summary     *seeddata.Summary
}

func NewHandler(db *database.DB, cfg *config.Config) *Handler {
	return &Handler{
		cfg: cfg,
		runner: func(ctx context.Context, mockDataDir string) (seeddata.Summary, error) {
			return seeddata.Run(ctx, db, mockDataDir)
		},
	}
}

func (h *Handler) Page(c *fiber.Ctx) error {
	if !h.cfg.AdminReseedEnabled {
		return fiber.ErrNotFound
	}

	return h.renderPage(c, pageData{
		Authorized:  h.isAuthorized(c),
		Environment: h.cfg.Environment,
		MockDataDir: h.cfg.MockDataDir,
	})
}

func (h *Handler) Login(c *fiber.Ctx) error {
	if !h.cfg.AdminReseedEnabled {
		return fiber.ErrNotFound
	}

	password := strings.TrimSpace(c.FormValue("password"))
	if subtle.ConstantTimeCompare([]byte(password), []byte(h.cfg.ReseedPagePassword)) != 1 {
		return h.renderPage(c.Status(fiber.StatusUnauthorized), pageData{
			Authorized:  false,
			Environment: h.cfg.Environment,
			MockDataDir: h.cfg.MockDataDir,
			Error:       "Invalid password",
		})
	}

	h.setAuthCookie(c)
	return c.Redirect("/reseed", fiber.StatusSeeOther)
}

func (h *Handler) Run(c *fiber.Ctx) error {
	if !h.cfg.AdminReseedEnabled {
		return fiber.ErrNotFound
	}

	if !h.isAuthorized(c) {
		return h.renderPage(c.Status(fiber.StatusUnauthorized), pageData{
			Authorized:  false,
			Environment: h.cfg.Environment,
			MockDataDir: h.cfg.MockDataDir,
			Error:       "Please unlock the reseed page first",
		})
	}

	startedAt := time.Now().UTC()
	log.Printf("browser reseed requested environment=%s ip=%s", h.cfg.Environment, c.IP())

	timeoutCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
	defer cancel()

	summary, err := h.runner(timeoutCtx, h.cfg.MockDataDir)
	if err != nil {
		log.Printf("browser reseed failed environment=%s ip=%s err=%v", h.cfg.Environment, c.IP(), err)
		return h.renderPage(c.Status(fiber.StatusInternalServerError), pageData{
			Authorized:  true,
			Environment: h.cfg.Environment,
			MockDataDir: h.cfg.MockDataDir,
			Error:       err.Error(),
		})
	}

	log.Printf("browser reseed completed environment=%s ip=%s users=%d assignments=%d student_answers=%d", h.cfg.Environment, c.IP(), summary.Users, summary.Assignments, summary.StudentAnswers)

	return h.renderPage(c, pageData{
		Authorized:  true,
		Environment: h.cfg.Environment,
		MockDataDir: h.cfg.MockDataDir,
		Success:     fmt.Sprintf("Reseed completed at %s UTC", startedAt.Format("2006-01-02 15:04:05")),
		Summary:     &summary,
	})
}

func (h *Handler) Logout(c *fiber.Ctx) error {
	if !h.cfg.AdminReseedEnabled {
		return fiber.ErrNotFound
	}

	h.clearAuthCookie(c)
	return c.Redirect("/reseed", fiber.StatusSeeOther)
}

func (h *Handler) isAuthorized(c *fiber.Ctx) bool {
	provided := strings.TrimSpace(c.Cookies(reseedCookieName))
	expected := h.authCookieValue()
	if provided == "" || expected == "" {
		return false
	}

	return subtle.ConstantTimeCompare([]byte(provided), []byte(expected)) == 1
}

func (h *Handler) setAuthCookie(c *fiber.Ctx) {
	c.Cookie(&fiber.Cookie{
		Name:     reseedCookieName,
		Value:    h.authCookieValue(),
		HTTPOnly: true,
		Secure:   h.cfg.IsProduction(),
		SameSite: fiber.CookieSameSiteLaxMode,
		Path:     "/",
		Expires:  time.Now().UTC().Add(12 * time.Hour),
	})
}

func (h *Handler) clearAuthCookie(c *fiber.Ctx) {
	c.Cookie(&fiber.Cookie{
		Name:     reseedCookieName,
		Value:    "",
		HTTPOnly: true,
		Secure:   h.cfg.IsProduction(),
		SameSite: fiber.CookieSameSiteLaxMode,
		Path:     "/",
		Expires:  time.Unix(0, 0),
		MaxAge:   -1,
	})
}

func (h *Handler) authCookieValue() string {
	sum := sha256.Sum256([]byte(h.cfg.JWTSecret + "|" + h.cfg.ReseedPagePassword))
	return hex.EncodeToString(sum[:])
}

func (h *Handler) renderPage(c *fiber.Ctx, data pageData) error {
	content := reseedPageHTML(data)
	c.Type("html", "utf-8")
	return c.SendString(content)
}

func reseedPageHTML(data pageData) string {
	var statusHTML strings.Builder
	if data.Error != "" {
		statusHTML.WriteString(`<div class="notice notice-error">` + html.EscapeString(data.Error) + `</div>`)
	}
	if data.Success != "" {
		statusHTML.WriteString(`<div class="notice notice-success">` + html.EscapeString(data.Success) + `</div>`)
	}
	if data.Summary != nil {
		statusHTML.WriteString(`<pre class="summary">`)
		statusHTML.WriteString(html.EscapeString(fmt.Sprintf(
			"users: %d\nclassrooms: %d\nquestions: %d\ntags: %d\nassignments: %d\nassignment_links: %d\nstudent_answers: %d\nmock_data_dir: %s",
			data.Summary.Users,
			data.Summary.Classrooms,
			data.Summary.Questions,
			data.Summary.Tags,
			data.Summary.Assignments,
			data.Summary.AssignmentLinks,
			data.Summary.StudentAnswers,
			data.Summary.MockDataDir,
		)))
		statusHTML.WriteString(`</pre>`)
	}

	var body strings.Builder
	if data.Authorized {
		body.WriteString(`
			<div class="card">
				<h2>Reseed database</h2>
				<p>This will clear seeded app data and repopulate it from Mock-Data.</p>
				<form method="post" action="/reseed/run">
					<button class="danger" type="submit">Reseed now</button>
				</form>
				<form method="post" action="/reseed/logout">
					<button type="submit">Lock page</button>
				</form>
			</div>
		`)
	} else {
		body.WriteString(`
			<div class="card">
				<h2>Unlock reseed</h2>
				<form method="post" action="/reseed/login">
					<label for="password">Password</label>
					<input id="password" name="password" type="password" autocomplete="current-password" required />
					<button type="submit">Unlock</button>
				</form>
			</div>
		`)
	}

	return fmt.Sprintf(`<!doctype html>
<html lang="en">
<head>
	<meta charset="utf-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1" />
	<title>BoostAI Reseed</title>
	<style>
		:root { color-scheme: dark; }
		body {
			margin: 0;
			font-family: Inter, Arial, sans-serif;
			background: #0f172a;
			color: #e2e8f0;
		}
		main {
			max-width: 720px;
			margin: 48px auto;
			padding: 0 20px 48px;
		}
		h1, h2 { margin-top: 0; }
		.card {
			background: #111827;
			border: 1px solid #334155;
			border-radius: 16px;
			padding: 24px;
			margin-bottom: 20px;
			box-shadow: 0 12px 30px rgba(0,0,0,0.25);
		}
		.meta {
			display: grid;
			grid-template-columns: 180px 1fr;
			gap: 8px 12px;
			font-size: 14px;
			margin-bottom: 20px;
		}
		.meta strong { color: #93c5fd; }
		label {
			display: block;
			margin-bottom: 8px;
			font-weight: 600;
		}
		input {
			width: 100%%;
			box-sizing: border-box;
			padding: 12px 14px;
			border-radius: 10px;
			border: 1px solid #475569;
			background: #0f172a;
			color: #e2e8f0;
			margin-bottom: 14px;
		}
		button {
			padding: 12px 16px;
			border-radius: 10px;
			border: 0;
			cursor: pointer;
			font-weight: 700;
			background: #38bdf8;
			color: #082f49;
			margin-right: 12px;
			margin-bottom: 12px;
		}
		button.danger {
			background: #f87171;
			color: #450a0a;
		}
		.notice {
			padding: 14px 16px;
			border-radius: 12px;
			margin-bottom: 16px;
			font-weight: 600;
		}
		.notice-error {
			background: rgba(220, 38, 38, 0.18);
			border: 1px solid rgba(248, 113, 113, 0.45);
			color: #fecaca;
		}
		.notice-success {
			background: rgba(22, 163, 74, 0.18);
			border: 1px solid rgba(74, 222, 128, 0.45);
			color: #bbf7d0;
		}
		.summary {
			background: #020617;
			border: 1px solid #334155;
			border-radius: 12px;
			padding: 16px;
			overflow: auto;
		}
	</style>
</head>
<body>
	<main>
		<div class="card">
			<h1>BoostAI reseed</h1>
			<div class="meta">
				<strong>Environment</strong><span>%s</span>
				<strong>Mock data path</strong><span>%s</span>
				<strong>Mode</strong><span>Browser-protected destructive reseed</span>
			</div>
			%s
		</div>
		%s
	</main>
</body>
</html>`,
		html.EscapeString(data.Environment),
		html.EscapeString(data.MockDataDir),
		statusHTML.String(),
		body.String(),
	)
}
