package tests

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/veypi/vbase"
	"github.com/veypi/vbase/cfg"
	"github.com/veypi/vbase/models"
	"github.com/veypi/vigo/contrib/config"
	"github.com/veypi/vigo/contrib/event"
)

const TestDBFile = "file::memory:?cache=shared"

// Global variables to hold test data
var (
	AdminToken string
	User1Token string
	User2Token string
	AdminID    string
	User1ID    string
	User2ID    string
)

func TestMain(m *testing.M) {
	// Setup
	setup()

	// Run tests
	code := m.Run()

	// Teardown
	teardown()

	os.Exit(code)
}

func setup() {
	// Configure for testing
	// Use in-memory SQLite database
	cfg.Global.DB = config.Database{
		Type: "sqlite",
		DSN:  TestDBFile,
	}
	// Use mock/memory Redis
	cfg.Global.Redis = config.Redis{
		Addr: "memory",
	}
	// Initialize DB connection and run migrations
	if err := models.Migrate(); err != nil {
		panic("failed to migrate database: " + err.Error())
	}

	// Initialize other components
	event.Start()
}

func teardown() {
	// No cleanup needed for in-memory database
}

// Helpers

func doRequest(t *testing.T, method, path string, body interface{}, token string) *httptest.ResponseRecorder {
	var bodyReader io.Reader
	if body != nil {
		jsonBody, err := json.Marshal(body)
		if err != nil {
			t.Fatalf("Failed to marshal body: %v", err)
		}
		bodyReader = bytes.NewReader(jsonBody)
	}

	req, err := http.NewRequest(method, path, bodyReader)
	if err != nil {
		t.Fatalf("Failed to create request: %v", err)
	}

	if token != "" {
		req.Header.Set("Authorization", "Bearer "+token)
	}
	req.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()

	// Create a vigo context to wrap the request
	// However, since we are testing the router, we should just serve HTTP
	vbase.Router.ServeHTTP(w, req)

	return w
}

func assertStatus(t *testing.T, w *httptest.ResponseRecorder, expectedCode int) {
	if w.Code != expectedCode {
		// Vigo might return 200 OK but with a JSON error code in body
		// Check body for error details
		t.Errorf("Expected HTTP status %d, got %d. Body: %s", expectedCode, w.Code, w.Body.String())
	}
}

// assertVigoCode checks the 'code' field in JSON response
func assertVigoCode(t *testing.T, w *httptest.ResponseRecorder, expectedCode int) {
	var resp struct {
		Code int `json:"code"`
	}
	if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
		t.Fatalf("Failed to unmarshal response: %v. Body: %s", err, w.Body.String())
	}
	if resp.Code != expectedCode {
		t.Errorf("Expected Vigo code %d, got %d. Body: %s", expectedCode, resp.Code, w.Body.String())
	}
}

// decodeResponse decodes the JSON response into v
func decodeResponse(t *testing.T, w *httptest.ResponseRecorder, v interface{}) {
	if err := json.Unmarshal(w.Body.Bytes(), v); err != nil {
		t.Fatalf("Failed to unmarshal response: %v. Body: %s", err, w.Body.String())
	}
}

// Common structs for responses
type BaseResp struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
}

type LoginResp struct {
	AccessToken  string `json:"access_token"`
	RefreshToken string `json:"refresh_token"`
	TokenType    string `json:"token_type"`
	ExpiresIn    int    `json:"expires_in"`
	User         struct {
		ID       string `json:"id"`
		Username string `json:"username"`
		Email    string `json:"email"`
	} `json:"user"`
}

type UserResp struct {
	ID       string `json:"id"`
	Username string `json:"username"`
	Nickname string `json:"nickname"`
	Email    string `json:"email"`
	RoleCode string `json:"role_code"`
}
