//go:build integration
// +build integration

package integration

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

	"github.com/golang-jwt/jwt/v5"
	"github.com/rede5/gohorsejobs/backend/internal/database"
	"github.com/rede5/gohorsejobs/backend/internal/router"
)

var testServer *httptest.Server

func TestMain(m *testing.M) {
	setTestEnv()
	database.InitDB()
	testServer = httptest.NewServer(router.NewRouter())
	defer testServer.Close()
	code := m.Run()
	cleanupTestData()
	os.Exit(code)
}

func setTestEnv() {
	if os.Getenv("DB_HOST") == "" {
		os.Setenv("DB_HOST", "db-60059.dc-sp-1.absamcloud.com")
	}
	if os.Getenv("DB_USER") == "" {
		os.Setenv("DB_USER", "yuki")
	}
	if os.Getenv("DB_PASSWORD") == "" {
		os.Setenv("DB_PASSWORD", "xl1zfmr6e9bb")
	}
	if os.Getenv("DB_NAME") == "" {
		os.Setenv("DB_NAME", "gohorsejobs_dev")
	}
	if os.Getenv("DB_PORT") == "" {
		os.Setenv("DB_PORT", "26868")
	}
	if os.Getenv("DB_SSLMODE") == "" {
		os.Setenv("DB_SSLMODE", "require")
	}
	if os.Getenv("JWT_SECRET") == "" {
		os.Setenv("JWT_SECRET", "gohorse-super-secret-key-2024-production")
	}
}

func cleanupTestData() {
	if database.DB != nil {
		database.DB.Exec("DELETE FROM applications WHERE id > 0")
		database.DB.Exec("DELETE FROM jobs WHERE title LIKE 'Int Test%'")
		database.DB.Exec("DELETE FROM companies WHERE name LIKE 'Int Test%'")
		database.DB.Exec("DELETE FROM users WHERE full_name LIKE 'Int Test%'")
	}
}

// Helpers

type testClient struct {
	baseURL string
	token   string
}

func newTestClient() *testClient {
	return &testClient{baseURL: testServer.URL}
}

func (c *testClient) setAuthToken(token string) {
	c.token = token
}

func (c *testClient) doRequest(method, path string, body interface{}) (*http.Response, error) {
	var reqBody io.Reader
	if body != nil {
		jsonData, err := json.Marshal(body)
		if err != nil {
			return nil, err
		}
		reqBody = bytes.NewReader(jsonData)
	}
	req, err := http.NewRequest(method, c.baseURL+path, reqBody)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")
	if c.token != "" {
		req.Header.Set("Authorization", "Bearer "+c.token)
	}
	return http.DefaultClient.Do(req)
}

func (c *testClient) get(path string) (*http.Response, error) {
	return c.doRequest("GET", path, nil)
}

func (c *testClient) post(path string, body interface{}) (*http.Response, error) {
	return c.doRequest("POST", path, body)
}

func (c *testClient) delete(path string) (*http.Response, error) {
	return c.doRequest("DELETE", path, nil)
}

func createAuthToken(t *testing.T, userID, tenantID string, roles []string) string {
	t.Helper()
	secret := os.Getenv("JWT_SECRET")
	claims := jwt.MapClaims{
		"sub":    userID,
		"tenant": tenantID,
		"roles":  roles,
		"iss":    "gohorse-jobs",
		"exp":    time.Now().Add(time.Hour).Unix(),
		"iat":    time.Now().Unix(),
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	str, err := token.SignedString([]byte(secret))
	if err != nil {
		t.Fatalf("Failed to sign token: %v", err)
	}
	return str
}

func setupTestCompanyAndUser(t *testing.T) (companyID, userID string) {
	t.Helper()

	// User
	err := database.DB.QueryRow(`
		INSERT INTO users (identifier, password_hash, role, full_name, email, created_at, updated_at)
		VALUES ($1, $2, $3, $4, $5, NOW(), NOW())
		ON CONFLICT (identifier) DO UPDATE SET full_name = $4
		RETURNING id`,
		"int-test-user", "hash", "superadmin", "Int Test User", "int@test.com",
	).Scan(&userID)
	if err != nil {
		t.Fatalf("Failed setup user: %v", err)
	}
	database.DB.Exec("INSERT INTO user_roles (user_id, role) VALUES ($1, 'superadmin') ON CONFLICT DO NOTHING", userID)

	// Company
	err = database.DB.QueryRow(`
		INSERT INTO companies (name, slug, type, active, verified, created_at, updated_at)
		VALUES ($1, $2, $3, $4, $5, NOW(), NOW())
		ON CONFLICT (slug) DO UPDATE SET name = $1
		RETURNING id`,
		"Int Test Company", "int-test-company", "employer", true, true,
	).Scan(&companyID)
	if err != nil {
		t.Fatalf("Failed setup company: %v", err)
	}

	return companyID, userID
}

func createTestCandidate(t *testing.T) string {
	var id string
	err := database.DB.QueryRow(`
		INSERT INTO users (identifier, password_hash, role, full_name, email, status, created_at, updated_at)
		VALUES ('int_cand_' || gen_random_uuid(), 'hash', 'candidate', 'Int Test Cand', 'cand@int.com', 'active', NOW(), NOW())
		RETURNING id
	`).Scan(&id)
	if err != nil {
		t.Fatalf("Failed create candidate: %v", err)
	}
	database.DB.Exec("INSERT INTO user_roles (user_id, role) VALUES ($1, 'candidate')", id)
	return id
}
