package httpserver_test

import (
	"bytes"
	"encoding/json"
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"

	apicontract "bit.admedia.com/scm/ad/adpilot-indexing-repo-sync.com/internal/contracts/api"
	httpserver "bit.admedia.com/scm/ad/adpilot-indexing-repo-sync.com/internal/http"
	"bit.admedia.com/scm/ad/adpilot-indexing-repo-sync.com/internal/service"
)

type mockImportAcceptor struct {
	req  apicontract.ImportRequest
	resp apicontract.ImportAcceptedResponse
	err  error
}

func (m *mockImportAcceptor) AcceptImport(_ *http.Request, req apicontract.ImportRequest) (apicontract.ImportAcceptedResponse, error) {
	m.req = req
	return m.resp, m.err
}

func sampleImportRequest() apicontract.ImportRequest {
	return apicontract.ImportRequest{
		ImportJobID:  "import-job-1",
		OrgID:        "org-1",
		ConnectionID: "conn-1",
		Provider:     "github",
		CloneAuth: apicontract.CloneAuth{
			Username: "x-access-token",
			Password: "ghp_secret",
		},
		Repos: []apicontract.ImportRepoRef{
			{
				ProviderRepoID: "123",
				FullName:       "acme/widget",
				DefaultBranch:  "main",
				CloneURL:       "https://github.com/acme/widget.git",
			},
		},
	}
}

func TestRepoImportAccepted(t *testing.T) {
	acceptor := &mockImportAcceptor{
		resp: apicontract.ImportAcceptedResponse{
			ImportJobID: "import-job-1",
			Status:      "accepted",
			Jobs: []apicontract.ImportRepoJob{
				{
					JobID:          "job_abc123",
					ProviderRepoID: "123",
					FullName:       "acme/widget",
					RepoID:         "github:acme/widget",
					Status:         "pending",
				},
			},
		},
	}
	server := httpserver.New("repo-sync-service", func() bool { return true }, nil, nil, acceptor, nil, nil, nil, "")

	body, _ := json.Marshal(sampleImportRequest())
	req := httptest.NewRequest(http.MethodPost, "/repos/import", bytes.NewReader(body))
	rec := httptest.NewRecorder()
	server.Handler().ServeHTTP(rec, req)

	if rec.Code != http.StatusAccepted {
		t.Fatalf("expected %d, got %d body=%s", http.StatusAccepted, rec.Code, rec.Body.String())
	}
	if acceptor.req.ImportJobID != "import-job-1" {
		t.Fatalf("importJobId: got %q", acceptor.req.ImportJobID)
	}

	var resp apicontract.ImportAcceptedResponse
	if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
		t.Fatalf("decode: %v", err)
	}
	if resp.Status != "accepted" || len(resp.Jobs) != 1 {
		t.Fatalf("unexpected response: %+v", resp)
	}
}

func TestRepoImportInvalidPayload(t *testing.T) {
	acceptor := &mockImportAcceptor{
		err: service.ErrInvalidImportPayload,
	}
	server := httpserver.New("repo-sync-service", func() bool { return true }, nil, nil, acceptor, nil, nil, nil, "")

	req := httptest.NewRequest(http.MethodPost, "/repos/import", bytes.NewReader([]byte(`{}`)))
	rec := httptest.NewRecorder()
	server.Handler().ServeHTTP(rec, req)

	if rec.Code != http.StatusBadRequest {
		t.Fatalf("expected %d, got %d", http.StatusBadRequest, rec.Code)
	}
}

func TestRepoImportDuplicate(t *testing.T) {
	existing := apicontract.ImportAcceptedResponse{
		ImportJobID: "import-job-1",
		Status:      "pending",
	}
	acceptor := &mockImportAcceptor{
		resp: existing,
		err:  service.ErrDuplicateImport,
	}
	server := httpserver.New("repo-sync-service", func() bool { return true }, nil, nil, acceptor, nil, nil, nil, "")

	body, _ := json.Marshal(sampleImportRequest())
	req := httptest.NewRequest(http.MethodPost, "/repos/import", bytes.NewReader(body))
	rec := httptest.NewRecorder()
	server.Handler().ServeHTTP(rec, req)

	if rec.Code != http.StatusConflict {
		t.Fatalf("expected %d, got %d", rec.Code, http.StatusConflict)
	}

	var resp apicontract.ImportAcceptedResponse
	if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
		t.Fatalf("decode: %v", err)
	}
	if resp.ImportJobID != "import-job-1" {
		t.Fatalf("expected existing job in body, got %+v", resp)
	}
}

func TestRepoImportInternalError(t *testing.T) {
	acceptor := &mockImportAcceptor{err: errors.New("store down")}
	server := httpserver.New("repo-sync-service", func() bool { return true }, nil, nil, acceptor, nil, nil, nil, "")

	body, _ := json.Marshal(sampleImportRequest())
	req := httptest.NewRequest(http.MethodPost, "/repos/import", bytes.NewReader(body))
	rec := httptest.NewRecorder()
	server.Handler().ServeHTTP(rec, req)

	if rec.Code != http.StatusInternalServerError {
		t.Fatalf("expected %d, got %d", rec.Code, http.StatusInternalServerError)
	}
}
