package qbit

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

// fakeSecrets is a minimal SecretsAccessor for tests.
type fakeSecrets struct{ pw string }

func (f fakeSecrets) Get(_ context.Context, _ string) ([]byte, error) {
	return []byte(f.pw), nil
}

func newFakeQbitServer(t *testing.T) *httptest.Server {
	t.Helper()
	mux := http.NewServeMux()
	mux.HandleFunc("/api/v2/auth/login", func(w http.ResponseWriter, r *http.Request) {
		_ = r.ParseForm()
		if r.Form.Get("password") != "correct" {
			_, _ = w.Write([]byte("Fails."))
			return
		}
		http.SetCookie(w, &http.Cookie{Name: "SID", Value: "session-token"})
		_, _ = w.Write([]byte("Ok."))
	})
	mux.HandleFunc("/api/v2/sync/maindata", func(w http.ResponseWriter, _ *http.Request) {
		_ = json.NewEncoder(w).Encode(MainData{
			RID:        1,
			FullUpdate: true,
			Torrents: map[string]Torrent{
				"abc123": {Hash: "abc123", Name: "Test", State: "uploading", Uploaded: 1024, Downloaded: 512, Ratio: 2.0},
			},
		})
	})
	mux.HandleFunc("/api/v2/torrents/info", func(w http.ResponseWriter, _ *http.Request) {
		_ = json.NewEncoder(w).Encode([]Torrent{
			{Hash: "abc123", Name: "Test"},
		})
	})
	mux.HandleFunc("/api/v2/torrents/trackers", func(w http.ResponseWriter, _ *http.Request) {
		_ = json.NewEncoder(w).Encode([]Tracker{
			{URL: "https://x.example/announce", Status: 2, NumPeers: 3},
		})
	})
	mux.HandleFunc("/api/v2/torrents/pause", func(w http.ResponseWriter, r *http.Request) {
		_ = r.ParseForm()
		if !strings.Contains(r.Form.Get("hashes"), "abc123") {
			w.WriteHeader(400)
			return
		}
		w.WriteHeader(200)
	})
	mux.HandleFunc("/api/v2/app/version", func(w http.ResponseWriter, _ *http.Request) {
		_, _ = w.Write([]byte("v5.0.0"))
	})
	return httptest.NewServer(mux)
}

func newQbitForTest(t *testing.T, srv *httptest.Server, pw string) TorrentClient {
	t.Helper()
	c, err := newQbitDirect(Config{
		ID:      "test-qbit",
		Name:    "Test",
		Type:    "qbit",
		BaseURL: srv.URL,
	}, Deps{Secrets: fakeSecrets{pw: pw}})
	if err != nil {
		t.Fatalf("newQbitDirect: %v", err)
	}
	return c
}

func TestLoginSuccess(t *testing.T) {
	srv := newFakeQbitServer(t)
	defer srv.Close()
	c := newQbitForTest(t, srv, "correct")
	if err := c.Login(context.Background()); err != nil {
		t.Fatalf("Login: %v", err)
	}
}

func TestLoginRejected(t *testing.T) {
	srv := newFakeQbitServer(t)
	defer srv.Close()
	c := newQbitForTest(t, srv, "wrong")
	if err := c.Login(context.Background()); err == nil {
		t.Fatal("expected login rejection")
	}
}

func TestSync(t *testing.T) {
	srv := newFakeQbitServer(t)
	defer srv.Close()
	c := newQbitForTest(t, srv, "correct")
	md, err := c.Sync(context.Background(), 0)
	if err != nil {
		t.Fatalf("Sync: %v", err)
	}
	if md.RID != 1 || !md.FullUpdate {
		t.Errorf("unexpected RID/full_update: %+v", md)
	}
	t1, ok := md.Torrents["abc123"]
	if !ok || t1.Ratio != 2.0 {
		t.Errorf("torrent missing or wrong: %+v", md.Torrents)
	}
}

func TestListAndGet(t *testing.T) {
	srv := newFakeQbitServer(t)
	defer srv.Close()
	c := newQbitForTest(t, srv, "correct")
	list, err := c.List(context.Background(), ListFilter{})
	if err != nil {
		t.Fatalf("List: %v", err)
	}
	if len(list) != 1 || list[0].Hash != "abc123" {
		t.Errorf("List: %+v", list)
	}
	got, err := c.Get(context.Background(), "abc123")
	if err != nil {
		t.Fatalf("Get: %v", err)
	}
	if got.Hash != "abc123" {
		t.Errorf("Get hash: %q", got.Hash)
	}
}

func TestTrackers(t *testing.T) {
	srv := newFakeQbitServer(t)
	defer srv.Close()
	c := newQbitForTest(t, srv, "correct")
	trs, err := c.Trackers(context.Background(), "abc123")
	if err != nil {
		t.Fatalf("Trackers: %v", err)
	}
	if len(trs) != 1 || trs[0].NumPeers != 3 {
		t.Errorf("Trackers: %+v", trs)
	}
}

func TestPause(t *testing.T) {
	srv := newFakeQbitServer(t)
	defer srv.Close()
	c := newQbitForTest(t, srv, "correct")
	if err := c.Pause(context.Background(), []string{"abc123"}); err != nil {
		t.Fatalf("Pause: %v", err)
	}
}

func TestHealth(t *testing.T) {
	srv := newFakeQbitServer(t)
	defer srv.Close()
	c := newQbitForTest(t, srv, "correct")
	if err := c.Health(context.Background()); err != nil {
		t.Fatalf("Health: %v", err)
	}
}

func TestUnknownType(t *testing.T) {
	if _, err := New(Config{Type: "no-such", BaseURL: "https://x"}, Deps{}); err == nil {
		t.Error("expected unknown-type error")
	}
}

func TestQuiTypeRegistered(t *testing.T) {
	srv := newFakeQbitServer(t)
	defer srv.Close()
	c, err := New(Config{ID: "x", Type: "qui", BaseURL: srv.URL}, Deps{Secrets: fakeSecrets{pw: "correct"}})
	if err != nil {
		t.Fatalf("New(qui): %v", err)
	}
	if err := c.Login(context.Background()); err != nil {
		t.Errorf("qui login: %v", err)
	}
}
