package http

import (
	"bytes"
	"context"
	"encoding/json"
	stdhttp "net/http"
	"testing"
	"time"

	"github.com/oklog/ulid/v2"

	"gitea.dcglab.co.uk/steve/restic-manager/internal/api"
	"gitea.dcglab.co.uk/steve/restic-manager/internal/store"
)

func TestRunNowForgetShipsRetentionGroupsAndDryRun(t *testing.T) {
	t.Parallel()
	srv, ts, st := rawTestServer(t)
	hostID, token := enrolHostForWS(t, srv, st, "manual-forget-host")
	seedInitJob(t, st, hostID)

	keepDaily := 7
	if err := st.CreateSourceGroup(context.Background(), &store.SourceGroup{
		ID:              ulid.Make().String(),
		HostID:          hostID,
		Name:            "documents",
		Includes:        []string{"/home/documents"},
		RetentionPolicy: store.RetentionPolicy{KeepDaily: &keepDaily},
	}); err != nil {
		t.Fatalf("create source group: %v", err)
	}

	c := agentDial(t, srv, ts, hostID, token)
	sendHello(t, c, "manual-forget-host")
	_ = drainUntil(t, c, api.MsgScheduleSet)

	body, err := json.Marshal(runNowRequest{Kind: api.JobForget, Args: []string{"--dry-run"}})
	if err != nil {
		t.Fatalf("marshal request: %v", err)
	}
	req, err := stdhttp.NewRequest(stdhttp.MethodPost, ts.URL+"/api/hosts/"+hostID+"/jobs", bytes.NewReader(body))
	if err != nil {
		t.Fatalf("new request: %v", err)
	}
	req.Header.Set("Content-Type", "application/json")
	req.AddCookie(loginAsAdmin(t, st))
	res, err := stdhttp.DefaultClient.Do(req)
	if err != nil {
		t.Fatalf("post run-now: %v", err)
	}
	defer res.Body.Close()
	if res.StatusCode != stdhttp.StatusAccepted {
		t.Fatalf("status: got %d, want %d", res.StatusCode, stdhttp.StatusAccepted)
	}

	got := readNextCommandRun(t, c, time.Now().Add(2*time.Second))
	if got == nil {
		t.Fatal("no command.run received")
	}
	if len(got.Args) != 1 || got.Args[0] != "--dry-run" {
		t.Fatalf("Args: got %q, want [--dry-run]", got.Args)
	}
	if len(got.ForgetGroups) != 1 {
		t.Fatalf("ForgetGroups: got %d, want 1", len(got.ForgetGroups))
	}
	group := got.ForgetGroups[0]
	if group.Tag != "documents" || group.Policy.KeepDaily == nil || *group.Policy.KeepDaily != 7 {
		t.Fatalf("ForgetGroups[0]: got %+v", group)
	}
}

func TestRunNowForgetRejectsHostWithoutRetention(t *testing.T) {
	t.Parallel()
	srv, ts, st := rawTestServer(t)
	hostID, token := enrolHostForWS(t, srv, st, "no-manual-retention-host")
	seedInitJob(t, st, hostID)

	c := agentDial(t, srv, ts, hostID, token)
	sendHello(t, c, "no-manual-retention-host")
	_ = drainUntil(t, c, api.MsgScheduleSet)

	body := []byte(`{"kind":"forget","args":["--dry-run"]}`)
	req, err := stdhttp.NewRequest(stdhttp.MethodPost, ts.URL+"/api/hosts/"+hostID+"/jobs", bytes.NewReader(body))
	if err != nil {
		t.Fatalf("new request: %v", err)
	}
	req.Header.Set("Content-Type", "application/json")
	req.AddCookie(loginAsAdmin(t, st))
	res, err := stdhttp.DefaultClient.Do(req)
	if err != nil {
		t.Fatalf("post run-now: %v", err)
	}
	defer res.Body.Close()
	if res.StatusCode != stdhttp.StatusUnprocessableEntity {
		t.Fatalf("status: got %d, want %d", res.StatusCode, stdhttp.StatusUnprocessableEntity)
	}

	var jobs int
	if err := st.DB().QueryRow(`SELECT COUNT(*) FROM jobs WHERE host_id = ? AND kind = 'forget'`, hostID).Scan(&jobs); err != nil {
		t.Fatalf("count forget jobs: %v", err)
	}
	if jobs != 0 {
		t.Fatalf("forget jobs: got %d, want 0", jobs)
	}
}
