package feichibridge

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

type fixedCaptchaSolver struct {
	code  string
	calls int
}

func (s *fixedCaptchaSolver) Solve(_ context.Context, image []byte) (string, error) {
	s.calls++
	if string(image) != "captcha-image" {
		return "", fmt.Errorf("unexpected captcha image")
	}
	return s.code, nil
}

func TestAPIClientUsesSessionHeadersAndExpectedEndpoints(t *testing.T) {
	var calls int
	server := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
		calls++
		if request.Header.Get("Cookie") != "R_SESS=test" || request.Header.Get("x-api-csrf") != "csrf" {
			t.Errorf("missing session headers: %#v", request.Header)
		}
		response.Header().Set("Content-Type", "application/json")
		switch {
		case request.URL.Path == "/api/v1/sys/vehicleRealStatuss":
			_ = json.NewEncoder(response).Encode(map[string]any{
				"code": 200,
				"data": []map[string]string{{"vehicleId": "id-1", "vin": "LTEST32960VIN0001"}},
			})
		case strings.Contains(request.URL.Path, "vehicleRealStatussByVId"):
			_ = json.NewEncoder(response).Encode(map[string]any{
				"code": 200,
				"data": map[string]any{"vehicleId": "id-1", "vin": "LTEST32960VIN0001", "dataItems": map[string]string{"2000": "2026-07-17 12:00:00"}},
			})
		case request.URL.Path == "/api/v1/sys/hisdataQuerys":
			_ = json.NewEncoder(response).Encode(map[string]any{
				"code": 200,
				"data": []map[string]any{{"subdata": []map[string]string{{"2000": "2026-07-17 11:59:50"}}}},
			})
		default:
			http.NotFound(response, request)
		}
	}))
	defer server.Close()

	client, err := NewAPIClient(server.URL, map[string]string{"Cookie": "R_SESS=test", "x-api-csrf": "csrf"}, time.Second)
	if err != nil {
		t.Fatal(err)
	}
	vehicles, err := client.Vehicles(context.Background())
	if err != nil || len(vehicles) != 1 {
		t.Fatalf("vehicles = %#v, err = %v", vehicles, err)
	}
	if _, err := client.Snapshot(context.Background(), "id-1"); err != nil {
		t.Fatal(err)
	}
	records, err := client.History(context.Background(), vehicles[0].VIN, time.Now().Add(-time.Hour), time.Now())
	if err != nil || len(records) != 1 {
		t.Fatalf("history = %#v, err = %v", records, err)
	}
	if calls != 3 {
		t.Fatalf("calls = %d", calls)
	}
}

func TestAPIClientMapsUnauthorizedEnvelope(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, _ *http.Request) {
		_, _ = response.Write([]byte(`{"code":401,"data":[]}`))
	}))
	defer server.Close()
	client, err := NewAPIClient(server.URL, nil, time.Second)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := client.Vehicles(context.Background()); err != ErrUnauthorized {
		t.Fatalf("error = %v", err)
	}
}

func TestAuthenticatedAPIClientLogsInAndRetriesExpiredSession(t *testing.T) {
	var loginCalls int
	var vehicleCalls int
	solver := &fixedCaptchaSolver{code: "12WN"}
	server := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
		response.Header().Set("Content-Type", "application/json")
		switch request.URL.Path {
		case "/api/v1/first-login":
			response.Header().Set("x-api-csrf", "csrf-value")
			response.Header().Add("Set-Cookie", "CSRF=csrf-value; Path=/")
			_, _ = response.Write([]byte(`{"code":200}`))
		case "/api/v1/login/randCode":
			if request.Header.Get("x-api-csrf") != "csrf-value" {
				t.Errorf("captcha CSRF = %q", request.Header.Get("x-api-csrf"))
			}
			src := "data:image/jpeg;base64," + base64.StdEncoding.EncodeToString([]byte("captcha-image"))
			_ = json.NewEncoder(response).Encode(map[string]any{"code": 200, "data": map[string]string{"src": src}})
		case "/api/v1/login":
			loginCalls++
			var payload map[string]string
			_ = json.NewDecoder(request.Body).Decode(&payload)
			if payload["username"] != "JXQN0003" || payload["validCode"] != "12WN" {
				t.Errorf("login payload = %#v", payload)
			}
			encrypted, err := base64.StdEncoding.DecodeString(payload["password"])
			if err != nil || len(encrypted) != 128 || payload["password"] == "JXQN0003-" {
				t.Errorf("password was not RSA encrypted: len=%d err=%v", len(encrypted), err)
			}
			_ = json.NewEncoder(response).Encode(map[string]any{
				"code": 200, "data": map[string]string{"token": fmt.Sprintf("token-%d", loginCalls)},
			})
		case "/api/v1/sys/vehicleRealStatuss":
			vehicleCalls++
			if vehicleCalls == 1 {
				_, _ = response.Write([]byte(`{"code":401,"data":[]}`))
				return
			}
			if request.Header.Get("Cookie") != "CSRF=csrf-value; R_SESS=token-2" {
				t.Errorf("session cookie = %q", request.Header.Get("Cookie"))
			}
			_, _ = response.Write([]byte(`{"code":200,"data":[]}`))
		default:
			http.NotFound(response, request)
		}
	}))
	defer server.Close()

	client, err := NewAuthenticatedAPIClient(
		server.URL,
		LoginCredentials{Username: "JXQN0003", Password: "JXQN0003-", MaxAttempts: 2},
		solver,
		time.Second,
	)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := client.Vehicles(context.Background()); err != nil {
		t.Fatal(err)
	}
	if loginCalls != 2 || solver.calls != 2 || vehicleCalls != 2 {
		t.Fatalf("login=%d solver=%d vehicles=%d", loginCalls, solver.calls, vehicleCalls)
	}
}
