package webui

import (
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"
)

// TestSettingsRejectsEnablingAdminMFAWithoutOwnMFA guards against a real self-lockout
// bug: enabling enforce_admin_mfa immediately blocks every admin route (including
// /settings itself) for any admin without their own MFA configured. Without this
// precondition, an admin could flip the toggle on and lock themselves out with no way
// back in short of editing the database directly.
func TestSettingsRejectsEnablingAdminMFAWithoutOwnMFA(t *testing.T) {
	app := newTestApp(t)
	mux := app.Mux()
	cookie := loginSession(t, app) // loginSession's admin has no MFA configured

	form := baseSettingsForm()
	form.Set("Auth.enforce_admin_mfa", "true")
	req := httptest.NewRequest(http.MethodPost, Prefix+"/settings_update", strings.NewReader(form.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.AddCookie(cookie)
	rec := httptest.NewRecorder()
	mux.ServeHTTP(rec, req)

	if rec.Code != http.StatusFound {
		t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
	}
	if got := app.Cfg.Section("Auth").Key("enforce_admin_mfa").Value(); got != "false" {
		t.Fatalf("enforce_admin_mfa = %q, want unchanged (still false) since the admin has no MFA", got)
	}

	// The same admin must still be able to reach every other page — the whole point
	// of rejecting the save is that nothing actually changed.
	req = httptest.NewRequest(http.MethodGet, Prefix+"/domains", nil)
	req.AddCookie(cookie)
	rec = httptest.NewRecorder()
	mux.ServeHTTP(rec, req)
	if rec.Code != http.StatusOK {
		t.Fatalf("expected /domains still reachable, got %d", rec.Code)
	}
}

// TestSettingsAllowsEnablingAdminMFAWithOwnMFA confirms the precondition isn't just a
// blanket rejection — an admin who already has MFA set up can turn enforcement on.
func TestSettingsAllowsEnablingAdminMFAWithOwnMFA(t *testing.T) {
	app := newTestApp(t)
	mux := app.Mux()
	cookie := loginSession(t, app)

	sess, err := app.DB.GetSession(cookie.Value)
	if err != nil || sess == nil {
		t.Fatal(err)
	}
	if err := app.DB.SetAdminTOTPSecret(sess.UserID, "JBSWY3DPEHPK3PXP", true); err != nil {
		t.Fatal(err)
	}

	form := baseSettingsForm()
	form.Set("Auth.enforce_admin_mfa", "true")
	req := httptest.NewRequest(http.MethodPost, Prefix+"/settings_update", strings.NewReader(form.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.AddCookie(cookie)
	rec := httptest.NewRecorder()
	mux.ServeHTTP(rec, req)

	if rec.Code != http.StatusFound {
		t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
	}
	if got := app.Cfg.Section("Auth").Key("enforce_admin_mfa").Value(); got != "true" {
		t.Fatalf("enforce_admin_mfa = %q, want true", got)
	}
}

// baseSettingsForm returns a full, valid settings_update submission (every field
// unchanged from newTestApp's fixture) — settingsUpdate iterates every existing ini
// key and only touches ones present in the form, but real form submissions always
// include every field on the page, so tests should too.
func baseSettingsForm() url.Values {
	return url.Values{
		"Server.smtp_port": {"4025"}, "Server.smtp_tls_port": {"40465"},
		"Server.web_http_port": {"5000"}, "Server.web_https_port": {"5001"},
		"Server.bind_ip": {"0.0.0.0"}, "Server.time_zone": {"UTC"},
		"Server.hostname": {"mail.example.com"}, "Server.helo_hostname": {"mail.example.com"},
		"Server.server_banner": {""},
		"Database.database_url": {"sqlite:///server_data/smtp_server.db"},
		"Logging.log_level": {"INFO"}, "Logging.hide_info_aiosmtpd": {"true"},
		"Relay.relay_timeout": {"30"},
		"TLS.tls_cert_file": {"ssl_certs/server.crt"}, "TLS.tls_key_file": {"ssl_certs/server.key"},
		"DKIM.dkim_key_size": {"2048"}, "DKIM.spf_server_ip": {"192.168.1.1"},
		"Attachments.attachments_path": {"server_data/attachments"},
		"IMAP.imap_port": {"1143"}, "IMAP.imap_tls_port": {"1993"},
		"Auth.enforce_admin_mfa": {"false"}, "Auth.enforce_mailbox_mfa": {"false"},
	}
}
