package auth

import (
	"context"
	"net/http"
	"net/http/httptest"
	"path/filepath"
	"testing"

	"ymhut-box/server/unified-management/internal/config"
	"ymhut-box/server/unified-management/internal/db"
)

func TestBootstrapShowsDefaultPasswordOnlyBeforeChange(t *testing.T) {
	root := t.TempDir()
	cfg := &config.Config{
		StorageDir: root,
		Database: config.DatabaseConfig{
			Provider:          "sqlite",
			SQLitePath:        root + "/test.sqlite",
			FailoverEnabled:   true,
			HealthIntervalSec: 3600,
		},
	}
	store, err := db.Open(cfg)
	if err != nil {
		t.Fatal(err)
	}
	defer store.Close()
	if err := store.EnsureDefaultAdmin(context.Background()); err != nil {
		t.Fatal(err)
	}
	service := NewService(store)
	payload, err := service.Bootstrap(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	if payload["isDefaultPassword"] != true || payload["defaultPassword"] != "admin" {
		t.Fatalf("unexpected bootstrap payload: %#v", payload)
	}
	if err := store.ChangeAdminPassword(context.Background(), "admin", "admin", "changed-password"); err != nil {
		t.Fatal(err)
	}
	payload, err = service.Bootstrap(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	if payload["isDefaultPassword"] != false || payload["defaultPassword"] != "" {
		t.Fatalf("default password leaked after change: %#v", payload)
	}
}

func TestChangeAdminPasswordPersistsAfterReopen(t *testing.T) {
	root := t.TempDir()
	dbPath := filepath.Join(root, "test.sqlite")
	cfg := &config.Config{
		StorageDir: root,
		Database: config.DatabaseConfig{
			Provider:          "sqlite",
			SQLitePath:        dbPath,
			FailoverEnabled:   true,
			HealthIntervalSec: 3600,
		},
	}
	store, err := db.Open(cfg)
	if err != nil {
		t.Fatal(err)
	}
	if err := store.EnsureDefaultAdmin(context.Background()); err != nil {
		t.Fatal(err)
	}
	if err := store.ChangeAdminPassword(context.Background(), "admin", "admin", "persisted-password"); err != nil {
		t.Fatal(err)
	}
	_ = store.Close()

	reopened, err := db.Open(cfg)
	if err != nil {
		t.Fatal(err)
	}
	defer reopened.Close()
	if _, ok, err := reopened.VerifyAdminPassword(context.Background(), "admin", "persisted-password"); err != nil || !ok {
		t.Fatalf("new password did not persist, ok=%v err=%v", ok, err)
	}
	if _, ok, err := reopened.VerifyAdminPassword(context.Background(), "admin", "admin"); err != nil || ok {
		t.Fatalf("old password still works, ok=%v err=%v", ok, err)
	}
}

func TestLoginLocksAfterRepeatedFailures(t *testing.T) {
	root := t.TempDir()
	cfg := &config.Config{
		StorageDir: root,
		Database: config.DatabaseConfig{
			Provider:          "sqlite",
			SQLitePath:        filepath.Join(root, "test.sqlite"),
			FailoverEnabled:   true,
			HealthIntervalSec: 3600,
		},
	}
	store, err := db.Open(cfg)
	if err != nil {
		t.Fatal(err)
	}
	defer store.Close()
	if err := store.EnsureDefaultAdmin(context.Background()); err != nil {
		t.Fatal(err)
	}
	service := NewService(store)
	for i := 0; i < loginMaxFails; i++ {
		if _, _, ok, err := service.Login(context.Background(), "admin", "wrong", "bad-captcha", "00000", "127.0.0.1"); err != nil || ok {
			t.Fatalf("failed login %d returned ok=%v err=%v", i, ok, err)
		}
	}
	captcha, err := service.NewCaptcha()
	if err != nil {
		t.Fatal(err)
	}
	service.mu.Lock()
	answer := service.captchas[captcha.ID].answer
	service.mu.Unlock()
	if _, _, ok, err := service.Login(context.Background(), "admin", "admin", captcha.ID, answer, "127.0.0.1"); err != nil || ok {
		t.Fatalf("locked login should fail without error, ok=%v err=%v", ok, err)
	}
}

func TestSessionCookieUsesSecureForForwardedHTTPS(t *testing.T) {
	req := httptest.NewRequest(http.MethodPost, "/api/admin/auth/login", nil)
	req.Header.Set("X-Forwarded-Proto", "https")
	res := httptest.NewRecorder()
	SetSessionCookieForRequest(res, req, "session-id")
	cookies := res.Result().Cookies()
	if len(cookies) != 1 {
		t.Fatalf("expected one cookie, got %d", len(cookies))
	}
	if !cookies[0].Secure {
		t.Fatalf("expected secure cookie for forwarded https: %#v", cookies[0])
	}
}
