//go:build integration

package migration_test

import (
	"context"
	"testing"

	"github.com/google/uuid"

	"github.com/ultisuite/ulti-backend/internal/integrationtest"
	"github.com/ultisuite/ulti-backend/internal/users"
)

func TestClaimInviteRequiresActiveProjectDomain(t *testing.T) {
	h := integrationtest.RequireHarness(t)
	ctx := context.Background()

	adminClient, adminClaims := integrationtest.RequireAdminClient(t, h)
	if _, err := users.EnsureUser(ctx, h.Pool, adminClaims); err != nil {
		t.Fatalf("ensure admin: %v", err)
	}
	if err := users.GrantPlatformAdmin(ctx, h.Pool, adminClaims.Sub); err != nil {
		t.Fatalf("grant admin: %v", err)
	}

	domainName := "migration-" + uuid.NewString()[:8] + ".test"
	var domainID string
	err := h.Pool.QueryRow(ctx, `
		INSERT INTO mail_domains (name, status, is_platform_domain)
		VALUES ($1, 'pending_verification', false)
		RETURNING id::text
	`, domainName).Scan(&domainID)
	integrationtest.FailIf(err, t, "insert domain")

	createResp, err := adminClient.Post("/api/v1/admin/migration/projects", map[string]any{
		"name":            "Domain-bound migration",
		"source_provider": "google",
		"domain_id":       domainID,
	})
	integrationtest.FailIf(err, t, "create project")
	integrationtest.FailUnlessStatus(t, createResp, 201)

	var created struct {
		ID string `json:"id"`
	}
	integrationtest.DecodeJSON(t, createResp, &created)

	actResp, err := adminClient.Post("/api/v1/admin/migration/projects/"+created.ID+"/activate", nil)
	integrationtest.FailIf(err, t, "activate project")
	integrationtest.FailUnlessStatus(t, actResp, 200)

	migrateeEmail := "user@" + domainName
	inviteResp, err := adminClient.Post("/api/v1/admin/migration/projects/"+created.ID+"/invites", map[string]string{
		"email": migrateeEmail,
	})
	integrationtest.FailIf(err, t, "create invite")
	integrationtest.FailUnlessStatus(t, inviteResp, 201)

	var invite struct {
		Token string `json:"token"`
	}
	integrationtest.DecodeJSON(t, inviteResp, &invite)

	migrateeClaims := integrationtest.RegularUser(integrationtest.NewExternalID("domain-claim"))
	migrateeClaims.Email = migrateeEmail
	migrateeClient, err := h.Client(migrateeClaims)
	integrationtest.FailIf(err, t, "migratee client")

	_, err = users.EnsureUser(ctx, h.Pool, migrateeClaims)
	integrationtest.FailIf(err, t, "ensure migratee")

	claimResp, err := migrateeClient.Post("/api/v1/migration/claim", map[string]string{
		"token":    invite.Token,
		"password": "test-password-123",
	})
	integrationtest.FailIf(err, t, "claim invite")
	if claimResp.Status != 400 {
		t.Fatalf("status = %d, want 400 for inactive domain; body=%s", claimResp.Status, string(claimResp.Body))
	}
}

func TestClaimInviteWithActiveDomainSucceeds(t *testing.T) {
	h := integrationtest.RequireHarness(t)
	ctx := context.Background()

	adminClient, adminClaims := integrationtest.RequireAdminClient(t, h)
	if _, err := users.EnsureUser(ctx, h.Pool, adminClaims); err != nil {
		t.Fatalf("ensure admin: %v", err)
	}
	if err := users.GrantPlatformAdmin(ctx, h.Pool, adminClaims.Sub); err != nil {
		t.Fatalf("grant admin: %v", err)
	}

	domainName := "active-" + uuid.NewString()[:8] + ".test"
	var domainID string
	err := h.Pool.QueryRow(ctx, `
		INSERT INTO mail_domains (name, status, is_platform_domain)
		VALUES ($1, 'active', false)
		RETURNING id::text
	`, domainName).Scan(&domainID)
	integrationtest.FailIf(err, t, "insert domain")

	createResp, err := adminClient.Post("/api/v1/admin/migration/projects", map[string]any{
		"name":            "Active domain migration",
		"source_provider": "google",
		"domain_id":       domainID,
	})
	integrationtest.FailIf(err, t, "create project")
	integrationtest.FailUnlessStatus(t, createResp, 201)

	var created struct {
		ID string `json:"id"`
	}
	integrationtest.DecodeJSON(t, createResp, &created)

	actResp, err := adminClient.Post("/api/v1/admin/migration/projects/"+created.ID+"/activate", nil)
	integrationtest.FailIf(err, t, "activate project")
	integrationtest.FailUnlessStatus(t, actResp, 200)

	migrateeEmail := "user@" + domainName
	inviteResp, err := adminClient.Post("/api/v1/admin/migration/projects/"+created.ID+"/invites", map[string]string{
		"email": migrateeEmail,
	})
	integrationtest.FailIf(err, t, "create invite")
	integrationtest.FailUnlessStatus(t, inviteResp, 201)

	var invite struct {
		Token string `json:"token"`
	}
	integrationtest.DecodeJSON(t, inviteResp, &invite)

	migrateeClaims := integrationtest.RegularUser(integrationtest.NewExternalID("domain-claim-ok"))
	migrateeClaims.Email = migrateeEmail
	migrateeClient, err := h.Client(migrateeClaims)
	integrationtest.FailIf(err, t, "migratee client")

	userID, err := users.EnsureUser(ctx, h.Pool, migrateeClaims)
	integrationtest.FailIf(err, t, "ensure migratee")

	claimResp, err := migrateeClient.Post("/api/v1/migration/claim", map[string]string{
		"token":    invite.Token,
		"password": "test-password-123",
	})
	integrationtest.FailIf(err, t, "claim invite")
	integrationtest.FailUnlessStatus(t, claimResp, 200)

	var mailboxCount int
	if err := h.Pool.QueryRow(ctx, `
		SELECT COUNT(*) FROM mailboxes mb
		JOIN mail_domains md ON md.id = mb.domain_id
		WHERE mb.user_id = $1::uuid AND md.id = $2::uuid
	`, userID, domainID).Scan(&mailboxCount); err != nil {
		t.Fatalf("count mailboxes: %v", err)
	}
	if mailboxCount != 1 {
		t.Fatalf("mailbox count = %d, want 1", mailboxCount)
	}
}
