package client

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

	"github.com/stretchr/testify/require"
)

func TestHTTPClientCreateBatchBuildsScannerContract(t *testing.T) {
	var captured *http.Request
	var body map[string]any
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		captured = r
		require.NoError(t, json.NewDecoder(r.Body).Decode(&body))
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusAccepted)
		_, _ = w.Write([]byte(`{"batch":{"batch_id":"scanner-batch-1","request_id":"client-key-1","source":"asset-platform","status":"queued","accepted_targets":1,"rejected_targets":0,"queued_jobs":1},"existed":false}`))
	}))
	defer server.Close()

	client, err := NewHTTPClient(Config{
		BaseURL:  server.URL,
		APIToken: "scanner-token",
		Now: func() time.Time {
			return time.Date(2026, 6, 9, 12, 0, 0, 0, time.UTC)
		},
	}, server.Client())
	require.NoError(t, err)

	resp, err := client.CreateBatch(context.Background(), CreateBatchRequest{
		RequestID: "client-key-1",
		Source:    DefaultSource,
		Targets: []CreateBatchTarget{{
			AssetID:   "42",
			PublicIP:  "8.8.8.8",
			Port:      443,
			Transport: "tcp",
			Metadata:  map[string]any{"should_not_send": true},
		}},
		Metadata: map[string]any{"should_not_send": true},
	})
	require.NoError(t, err)
	require.Equal(t, "scanner-batch-1", resp.BatchID)
	require.Equal(t, 1, resp.AcceptedTargets)
	require.NotNil(t, captured)
	require.Equal(t, http.MethodPost, captured.Method)
	require.Equal(t, "/api/v1/reachability/batches", captured.URL.Path)
	require.Equal(t, "Bearer scanner-token", captured.Header.Get("Authorization"))
	require.Equal(t, "application/json", captured.Header.Get("Content-Type"))
	require.Equal(t, "application/json", captured.Header.Get("Accept"))
	require.Equal(t, "client-key-1", captured.Header.Get("Idempotency-Key"))
	require.Equal(t, "2026-06-09T12:00:00Z", captured.Header.Get("X-Request-Timestamp"))
	require.Equal(t, "client-key-1", body["request_id"])
	require.Equal(t, DefaultSource, body["source"])
	require.NotContains(t, body, "metadata")
	require.Len(t, body["targets"], 1)
	targets, ok := body["targets"].([]any)
	require.True(t, ok)
	target, ok := targets[0].(map[string]any)
	require.True(t, ok)
	require.NotContains(t, target, "metadata")
}

func TestHTTPClientCancelBatchPostsScannerCancel(t *testing.T) {
	var captured *http.Request
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		captured = r
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte(`{"batch":{"batch_id":"scanner-batch-1","request_id":"client-key-1","source":"asset-platform","status":"canceled"}}`))
	}))
	defer server.Close()

	client, err := NewHTTPClient(Config{BaseURL: server.URL, APIToken: "scanner-token"}, server.Client())
	require.NoError(t, err)

	resp, err := client.CancelBatch(context.Background(), "scanner-batch-1", "client-key-1-cancel")
	require.NoError(t, err)
	require.Equal(t, "scanner-batch-1", resp.BatchID)
	require.Equal(t, "canceled", resp.Status)
	require.NotNil(t, captured)
	require.Equal(t, http.MethodPost, captured.Method)
	require.Equal(t, "/api/v1/reachability/batches/scanner-batch-1/cancel", captured.URL.Path)
	require.Equal(t, "Bearer scanner-token", captured.Header.Get("Authorization"))
	require.Equal(t, "client-key-1-cancel", captured.Header.Get("Idempotency-Key"))
}

func TestHTTPClientDecodesDastScanResultsShape(t *testing.T) {
	completedAt := "2026-06-09T12:00:01Z"
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		require.Equal(t, "/api/v1/reachability/batches/scanner-batch-1/results", r.URL.Path)
		w.Header().Set("Content-Type", "application/json")
		_, _ = w.Write([]byte(`{
			"batch":{
				"batch_id":"scanner-batch-1",
				"request_id":"client-key-1",
				"source":"asset-platform",
				"status":"completed",
				"updated_at":"` + completedAt + `",
				"succeeded_jobs":1
			},
			"observations":[{
				"observation_id":"obs-1",
				"asset_id":"42",
				"target_id":"target-1",
				"job_id":"job-1",
				"public_ip":"8.8.8.8",
				"port":443,
				"transport":"tcp",
				"result":"confirmed_open",
				"reason_code":"tcp_connected",
				"connect_ms":24,
				"scanner_id":"scanner-1",
				"scanner_version":"1.0.0",
				"scanner_source_ip":"203.0.113.10",
				"evidence_path":"mock://evidence",
				"evidence_sha256":"abc",
				"started_at":"2026-06-09T12:00:00Z",
				"finished_at":"2026-06-09T12:00:01Z",
				"created_at":"2026-06-09T12:00:01Z"
			}]
		}`))
	}))
	defer server.Close()

	client, err := NewHTTPClient(Config{BaseURL: server.URL, APIToken: "scanner-token"}, server.Client())
	require.NoError(t, err)

	resp, err := client.GetResults(context.Background(), "scanner-batch-1")
	require.NoError(t, err)
	require.Equal(t, "scanner-batch-1", resp.BatchID)
	require.Equal(t, "client-key-1", resp.RequestID)
	require.Equal(t, "completed", resp.Status)
	require.Len(t, resp.Observations, 1)
	require.Equal(t, "tcp_connected", resp.Observations[0].ReasonCode)
}
