package pulls

import (
	"context"
	"encoding/json"
	"io"
	"log/slog"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/bluesky-social/indigo/atproto/syntax"
	"tangled.org/core/api/tangled"
	"tangled.org/core/appview/config"
	"tangled.org/core/appview/models"
	"tangled.org/core/appview/pages"
)

const (
	resubmitTestOwnerDID  = "did:plc:boltless"
	resubmitTestRepoDID   = "did:plc:akshay"
	resubmitTestBranch    = "feature"
	resubmitTestSourceRev = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
)

func newKnotmirrorStub(t *testing.T, hash string) *httptest.Server {
	t.Helper()
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/xrpc/"+tangled.GitTempGetBranchNSID {
			t.Errorf("unexpected path %q", r.URL.Path)
			http.Error(w, "wrong path", http.StatusNotFound)
			return
		}
		repoQuery := r.URL.Query().Get("repo")
		if _, err := syntax.ParseDID(repoQuery); err != nil {
			t.Errorf("repo param %q is not a DID: %v", repoQuery, err)
			http.Error(w, "repo must be a DID", http.StatusBadRequest)
			return
		}
		if got := r.URL.Query().Get("name"); got != resubmitTestBranch {
			t.Errorf("name param = %q, want %q", got, resubmitTestBranch)
		}
		w.Header().Set("Content-Type", "application/json")
		_ = json.NewEncoder(w).Encode(tangled.GitTempGetBranch_Output{
			Name: resubmitTestBranch,
			Hash: hash,
			When: time.Now().UTC().Format(time.RFC3339),
		})
	}))
}

func newPullsFromKnotURL(url string) *Pulls {
	return &Pulls{
		logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
		config: &config.Config{
			KnotMirror: config.KnotMirrorConfig{Url: url},
		},
	}
}

func newForkPull(state models.PullState) (*models.Pull, *models.Repo) {
	sourceRepoDid := syntax.DID(resubmitTestRepoDID)
	sourceBranch := resubmitTestBranch
	pull := &models.Pull{
		State:        state,
		OwnerDid:     resubmitTestOwnerDID,
		TargetBranch: "main",
		Versions: []models.PullVersion{
			{Head: resubmitTestSourceRev},
		},
		SourceBranch: &sourceBranch,
		SourceRepo:   sourceRepoDid,
	}
	repo := &models.Repo{RepoDid: resubmitTestRepoDID}
	return pull, repo
}

func TestResubmitCheck_BranchAdvanced(t *testing.T) {
	srv := newKnotmirrorStub(t, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
	defer srv.Close()

	s := newPullsFromKnotURL(srv.URL)
	ctx := t.Context()
	pull, repo := newForkPull(models.PullOpen)

	got := s.resubmitCheck(ctx, pull)
	if got != pages.ShouldResubmit {
		t.Errorf("resubmitCheck() = %v, want ShouldResubmit", got)
	}
}

func TestResubmitCheck_BranchUpToDate(t *testing.T) {
	srv := newKnotmirrorStub(t, resubmitTestSourceRev)
	defer srv.Close()

	s := newPullsFromKnotURL(srv.URL)
	ctx := t.Context()
	pull, repo := newForkPull(models.PullOpen)

	got := s.resubmitCheck(ctx, pull)
	if got != pages.ShouldNotResubmit {
		t.Errorf("resubmitCheck() = %v, want ShouldNotResubmit", got)
	}
}

func TestResubmitCheck_MergedReturnsUnknown(t *testing.T) {
	s := newPullsFromKnotURL("http://unused")
	ctx := t.Context()
	pull, repo := newForkPull(models.PullMerged)

	if got := s.resubmitCheck(ctx, pull); got != pages.Unknown {
		t.Errorf("resubmitCheck() = %v, want Unknown for merged pull", got)
	}
}

func TestResubmitCheck_PatchBasedReturnsUnknown(t *testing.T) {
	s := newPullsFromKnotURL("http://unused")
	ctx := t.Context()
	pull, repo := newForkPull(models.PullOpen)
	pull.SourceBranch = nil

	if got := s.resubmitCheck(ctx, pull); got != pages.Unknown {
		t.Errorf("resubmitCheck() = %v, want Unknown for patch-based pull", got)
	}
}

func TestResubmitCheck_KnotUnreachableReturnsUnknown(t *testing.T) {
	s := newPullsFromKnotURL("http://127.0.0.1:1")
	ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
	defer cancel()
	pull, repo := newForkPull(models.PullOpen)

	if got := s.resubmitCheck(ctx, pull); got != pages.Unknown {
		t.Errorf("resubmitCheck() = %v, want Unknown when knot unreachable", got)
	}
}
