/**
 * checkout-flows.spec.ts — Comprehensive checkout + auth flow tests.
 *
 * Covers:
 *   - All three plan variants (free, professional, founding)
 *   - Page structure, pricing, features list, noindex, canonical
 *   - Auth section / payment section initial visibility per plan
 *   - Form validation (empty fields, short password)
 *   - Sign in / sign up toggle
 *   - Mocked auth flows: sign-up success, email-confirm-required,
 *     existing session, sign-in error
 *   - DOM transitions: auth-section hidden → payment/free-success shown
 *   - Auth badge rendered with user name after auth
 *   - buyer-email pre-filled from Supabase user object
 *   - /api/checkout endpoint response shapes
 *   - /api/supabase-config CORS enforcement
 *   - Edge cases: unknown plan, no plan param
 *
 * Network mocking strategy: Playwright route() intercepts
 *   - GET /api/supabase-config → returns fake Supabase URL + anon key
 *   - GET  <fake-supabase>/auth/v1/user → no session or mock user
 *   - POST <fake-supabase>/auth/v1/signup → success or email-confirm
 *   - POST <fake-supabase>/auth/v1/token  → sign-in success or error
 * This lets us test full JS-driven DOM transitions without real credentials.
 */

import { test, expect, type Page } from '@playwright/test';

// ─── Mock helpers ────────────────────────────────────────────────────────────

const FAKE_SUPA_URL = 'https://xyzfaketest.supabase.co';
const FAKE_ANON_KEY = 'fake-anon-key-for-playwright-testing';

const MOCK_USER = {
  id: 'test-uid-playwright-001',
  email: 'playwright@example.com',
  user_metadata: { full_name: 'Playwright Tester' },
};

const MOCK_SESSION = {
  access_token: 'fake-access-token-playwright',
  refresh_token: 'fake-refresh-token-playwright',
  token_type: 'bearer',
  expires_in: 3600,
  user: MOCK_USER,
};

async function mockSupabaseConfig(page: Page) {
  await page.route('/api/supabase-config', (route) =>
    route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({ url: FAKE_SUPA_URL, anon_key: FAKE_ANON_KEY }),
    })
  );
}

async function mockNoSession(page: Page) {
  await page.route(`${FAKE_SUPA_URL}/auth/v1/user`, (route) =>
    route.fulfill({
      status: 401,
      contentType: 'application/json',
      body: JSON.stringify({ error: 'not_authenticated', message: 'JWT expired' }),
    })
  );
}

async function mockExistingSession(page: Page) {
  // Pre-seed localStorage with a fake Supabase session so getUser() fires
  // the /auth/v1/user HTTP request (Supabase v2 only calls the endpoint when
  // a stored token exists). Key format: sb-{projectRef}-auth-token.
  await page.addInitScript(([supaUrl, mockUser, mockSession]: [string, typeof MOCK_USER, typeof MOCK_SESSION]) => {
    const ref = new URL(supaUrl).hostname.split('.')[0]; // "xyzfaketest"
    const stored = {
      access_token:  mockSession.access_token,
      token_type:    'bearer',
      expires_in:    3600,
      expires_at:    Math.floor(Date.now() / 1000) + 3600,
      refresh_token: mockSession.refresh_token,
      user:          mockUser,
    };
    localStorage.setItem(`sb-${ref}-auth-token`, JSON.stringify(stored));
  }, [FAKE_SUPA_URL, MOCK_USER, MOCK_SESSION] as [string, typeof MOCK_USER, typeof MOCK_SESSION]);

  // Mock the /auth/v1/user endpoint that Supabase calls to validate the token
  await page.route(`${FAKE_SUPA_URL}/auth/v1/user`, (route) =>
    route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify(MOCK_USER),
    })
  );
}

async function mockSignUpSuccess(page: Page) {
  await page.route(`${FAKE_SUPA_URL}/auth/v1/signup`, (route) =>
    route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({
        data: { session: MOCK_SESSION, user: MOCK_USER },
        error: null,
        ...MOCK_SESSION,
      }),
    })
  );
}

async function mockSignUpEmailConfirmRequired(page: Page) {
  await page.route(`${FAKE_SUPA_URL}/auth/v1/signup`, (route) =>
    route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({
        data: { session: null, user: MOCK_USER },
        error: null,
      }),
    })
  );
}

async function mockSignInSuccess(page: Page) {
  await page.route(`${FAKE_SUPA_URL}/auth/v1/token*`, (route) =>
    route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify(MOCK_SESSION),
    })
  );
}

async function mockSignInError(page: Page, message = 'Invalid login credentials') {
  await page.route(`${FAKE_SUPA_URL}/auth/v1/token**`, (route) =>
    route.fulfill({
      status: 400,
      contentType: 'application/json',
      body: JSON.stringify({ error: 'invalid_grant', error_description: message }),
    })
  );
}

// ─── Per-plan structure ───────────────────────────────────────────────────────

for (const plan of ['free', 'professional', 'founding'] as const) {
  test(`[${plan}] page loads 200 with content`, async ({ page }) => {
    const res = await page.goto(`/checkout?plan=${plan}`);
    expect(res?.status()).toBe(200);
    await expect(page.locator('body')).not.toBeEmpty();
  });

  test(`[${plan}] page has non-empty title`, async ({ page }) => {
    await page.goto(`/checkout?plan=${plan}`);
    expect((await page.title()).trim().length).toBeGreaterThan(0);
  });

  test(`[${plan}] nav back link to /`, async ({ page }) => {
    await page.goto(`/checkout?plan=${plan}`);
    await expect(page.locator('nav a[href="/"]').first()).toBeAttached();
  });

  test(`[${plan}] canonical is production URL — not stage/run.app`, async ({ page }) => {
    await page.goto(`/checkout?plan=${plan}`);
    const canonical = await page.locator('link[rel="canonical"]').getAttribute('href');
    expect(canonical).toContain('neurontechnologies.ai');
    expect(canonical).not.toMatch(/run\.app|stage/);
  });

  test(`[${plan}] noindex meta tag present`, async ({ page }) => {
    await page.goto(`/checkout?plan=${plan}`);
    const robots = await page.locator('meta[name="robots"]').getAttribute('content');
    expect(robots).toContain('noindex');
  });

  test(`[${plan}] Google + GitHub social buttons present`, async ({ page }) => {
    await page.goto(`/checkout?plan=${plan}`);
    await expect(page.locator('#btn-google')).toBeAttached();
    await expect(page.locator('#btn-github')).toBeAttached();
  });

  test(`[${plan}] email + password inputs present`, async ({ page }) => {
    await page.goto(`/checkout?plan=${plan}`);
    await expect(page.locator('#auth-email')).toBeAttached();
    await expect(page.locator('#auth-password')).toBeAttached();
  });

  test(`[${plan}] auth message div present`, async ({ page }) => {
    await page.goto(`/checkout?plan=${plan}`);
    await expect(page.locator('#auth-message')).toBeAttached();
  });

  test(`[${plan}] auth badge container in DOM`, async ({ page }) => {
    await page.goto(`/checkout?plan=${plan}`);
    await expect(page.locator('#auth-badge')).toBeAttached();
  });
}

// ─── Plan-specific content ────────────────────────────────────────────────────

test('[professional] shows $19 / month pricing', async ({ page }) => {
  await page.goto('/checkout?plan=professional');
  const body = (await page.locator('body').textContent()) ?? '';
  expect(body).toContain('$19');
  expect(body.toLowerCase()).toContain('month');
});

test('[professional] features include persistent memory + API keys', async ({ page }) => {
  await page.goto('/checkout?plan=professional');
  const body = (await page.locator('body').textContent()) ?? '';
  expect(body).toContain('Persistent memory');
  expect(body).toContain('Bring your own API keys');
});

test('[founding] shows $199 one-time pricing', async ({ page }) => {
  await page.goto('/checkout?plan=founding');
  const body = (await page.locator('body').textContent()) ?? '';
  expect(body).toContain('$199');
  expect(body.toLowerCase()).toContain('one-time');
});

test('[founding] features include founding badge + lifetime', async ({ page }) => {
  await page.goto('/checkout?plan=founding');
  const body = (await page.locator('body').textContent()) ?? '';
  expect(body).toContain('Founding member badge');
  expect(body.toLowerCase()).toContain('lifetime');
});

test('[free] shows free / no card pricing', async ({ page }) => {
  await page.goto('/checkout?plan=free');
  const body = (await page.locator('body').textContent()) ?? '';
  expect(body.toLowerCase()).toMatch(/\$0|free|no card/);
});

test('[free] features include persistent memory + BYOAPI', async ({ page }) => {
  await page.goto('/checkout?plan=free');
  const body = (await page.locator('body').textContent()) ?? '';
  expect(body).toContain('Persistent memory');
});

// ─── Initial visibility per plan ─────────────────────────────────────────────

test('[free] auth-section visible on load (account creation flow)', async ({ page }) => {
  await page.goto('/checkout?plan=free');
  await expect(page.locator('#auth-section')).toBeVisible();
});

test('[free] payment-section hidden on load (shown after auth)', async ({ page }) => {
  await page.goto('/checkout?plan=free');
  const ps = page.locator('#payment-section');
  if (await ps.count() > 0) {
    await expect(ps).toBeHidden();
  }
});

test('[free] payment-element container present (Stripe mounts here)', async ({ page }) => {
  await page.goto('/checkout?plan=free');
  await expect(page.locator('#payment-element')).toBeAttached();
});

test('[professional] payment-section visible on load', async ({ page }) => {
  await page.goto('/checkout?plan=professional');
  await expect(page.locator('#payment-section')).toBeVisible();
});

test('[professional] auth-section hidden on load (optional for paid)', async ({ page }) => {
  await page.goto('/checkout?plan=professional');
  await expect(page.locator('#auth-section')).toBeHidden();
});

test('[founding] payment-section visible on load', async ({ page }) => {
  await page.goto('/checkout?plan=founding');
  await expect(page.locator('#payment-section')).toBeVisible();
});

test('[founding] auth-section hidden on load (optional for paid)', async ({ page }) => {
  await page.goto('/checkout?plan=founding');
  await expect(page.locator('#auth-section')).toBeHidden();
});

// ─── Payment form elements (paid plans) ──────────────────────────────────────

for (const plan of ['professional', 'founding'] as const) {
  test(`[${plan}] payment-element container present (Stripe mounts here)`, async ({ page }) => {
    await page.goto(`/checkout?plan=${plan}`);
    await expect(page.locator('#payment-element')).toBeAttached();
  });

  test(`[${plan}] buyer-email input present`, async ({ page }) => {
    await page.goto(`/checkout?plan=${plan}`);
    await expect(page.locator('#buyer-email')).toBeAttached();
  });

  test(`[${plan}] submit/pay button present`, async ({ page }) => {
    await page.goto(`/checkout?plan=${plan}`);
    const submitBtn = page.locator('#submit-btn, .checkout-submit, button[type="submit"]').first();
    await expect(submitBtn).toBeAttached();
  });
}

// ─── Form validation ──────────────────────────────────────────────────────────

test('[free] submit with empty email shows auth error', async ({ page }) => {
  await mockSupabaseConfig(page);
  await page.goto('/checkout?plan=free');
  await page.waitForLoadState('domcontentloaded');
  await page.locator('.checkout-email-btn').click();
  const msg = page.locator('#auth-message');
  await expect(msg).toBeVisible({ timeout: 4000 });
  const text = (await msg.textContent()) ?? '';
  expect(text.toLowerCase()).toMatch(/email|password|enter|required/);
});

test('[free] submit with password < 8 chars shows length error', async ({ page }) => {
  await mockSupabaseConfig(page);
  await page.goto('/checkout?plan=free');
  await page.waitForLoadState('domcontentloaded');
  await page.fill('#auth-email', 'test@example.com');
  await page.fill('#auth-password', 'short');
  await page.locator('.checkout-email-btn').click();
  const msg = page.locator('#auth-message');
  await expect(msg).toBeVisible({ timeout: 4000 });
  const text = (await msg.textContent()) ?? '';
  expect(text).toContain('8');
});

test('[free] submit with email only (no password) shows error', async ({ page }) => {
  await mockSupabaseConfig(page);
  await page.goto('/checkout?plan=free');
  await page.waitForLoadState('domcontentloaded');
  await page.fill('#auth-email', 'test@example.com');
  // leave password empty
  await page.locator('.checkout-email-btn').click();
  const msg = page.locator('#auth-message');
  await expect(msg).toBeVisible({ timeout: 4000 });
});

// ─── Sign in / sign up toggle ─────────────────────────────────────────────────

test('[free] initial button says "Create account"', async ({ page }) => {
  await page.goto('/checkout?plan=free');
  await expect(page.locator('.checkout-email-btn')).toContainText('Create account');
});

test('[free] clicking "Sign in" link changes button text to "Sign in"', async ({ page }) => {
  await page.goto('/checkout?plan=free');
  await page.waitForLoadState('domcontentloaded');
  await page.click('a[onclick*="showSignIn"]');
  await expect(page.locator('.checkout-email-btn')).toContainText('Sign in');
});

test('[free] divider label changes for email mode', async ({ page }) => {
  await page.goto('/checkout?plan=free');
  await expect(page.locator('#auth-divider-label')).toContainText(/email|account/i);
});

// ─── Mocked free-plan auth flows ──────────────────────────────────────────────

test('[free] successful sign-up → payment-section shown, auth-section hidden', async ({ page }) => {
  await mockSupabaseConfig(page);
  await mockSignUpSuccess(page);
  await page.goto('/checkout?plan=free');
  await page.waitForLoadState('domcontentloaded');

  await page.fill('#auth-email', 'newuser@example.com');
  await page.fill('#auth-password', 'password123');
  await page.locator('.checkout-email-btn').click();

  await expect(page.locator('#payment-section')).toBeVisible({ timeout: 6000 });
  await expect(page.locator('#auth-section')).toBeHidden();
});

test('[free] sign-up email-confirm-required → shows check-email message', async ({ page }) => {
  await mockSupabaseConfig(page);
  await mockSignUpEmailConfirmRequired(page);
  await page.goto('/checkout?plan=free');
  await page.waitForLoadState('domcontentloaded');

  await page.fill('#auth-email', 'confirm@example.com');
  await page.fill('#auth-password', 'password123');
  await page.locator('.checkout-email-btn').click();

  const msg = page.locator('#auth-message');
  await expect(msg).toBeVisible({ timeout: 6000 });
  const text = (await msg.textContent()) ?? '';
  expect(text.toLowerCase()).toMatch(/email|confirm|check/);
});

test('[free] sign-in success (via toggle) → payment-section shown', async ({ page }) => {
  await mockSupabaseConfig(page);
  await mockSignInSuccess(page);
  await page.goto('/checkout?plan=free');
  await page.waitForLoadState('domcontentloaded');

  await page.click('a[onclick*="showSignIn"]');
  await page.fill('#auth-email', 'existing@example.com');
  await page.fill('#auth-password', 'password123');
  await page.locator('.checkout-email-btn').click();

  await expect(page.locator('#payment-section')).toBeVisible({ timeout: 6000 });
});

test('[free] sign-in error → shows error message, form stays visible', async ({ page }) => {
  await mockSupabaseConfig(page);
  await mockSignInError(page, 'Invalid login credentials');
  await page.goto('/checkout?plan=free');
  await page.waitForLoadState('domcontentloaded');

  await page.click('a[onclick*="showSignIn"]');
  await page.fill('#auth-email', 'wrong@example.com');
  await page.fill('#auth-password', 'wrongpassword');
  await page.locator('.checkout-email-btn').click();

  const msg = page.locator('#auth-message');
  await expect(msg).toBeVisible({ timeout: 6000 });
  const text = (await msg.textContent()) ?? '';
  expect(text.toLowerCase()).toMatch(/invalid|credential|incorrect|error/);
});

// ─── Mocked paid-plan auth flows ─────────────────────────────────────────────

for (const plan of ['professional', 'founding'] as const) {
  test(`[${plan}] existing session → auth badge visible with user info`, async ({ page }) => {
    await mockSupabaseConfig(page);
    await mockExistingSession(page);
    await page.goto(`/checkout?plan=${plan}`);

    const badge = page.locator('#auth-badge');
    await expect(badge).toBeVisible({ timeout: 6000 });
    const text = (await badge.textContent()) ?? '';
    expect(text).toMatch(/Playwright Tester|playwright@example\.com/);
  });

  test(`[${plan}] existing session → buyer-email pre-filled`, async ({ page }) => {
    await mockSupabaseConfig(page);
    await mockExistingSession(page);
    await page.goto(`/checkout?plan=${plan}`);

    await page.waitForFunction(
      () => {
        const el = document.getElementById('buyer-email') as HTMLInputElement | null;
        return el !== null && el.value.includes('@');
      },
      { timeout: 6000 }
    );
    const val = await page.locator('#buyer-email').inputValue();
    expect(val).toBe('playwright@example.com');
  });

  test(`[${plan}] existing session → auth-section hidden`, async ({ page }) => {
    await mockSupabaseConfig(page);
    await mockExistingSession(page);
    await page.goto(`/checkout?plan=${plan}`);

    // After session is detected auth-section stays/becomes hidden
    await page.waitForTimeout(2000); // let JS run
    await expect(page.locator('#auth-section')).toBeHidden();
  });

  test(`[${plan}] existing session → payment-section remains visible`, async ({ page }) => {
    await mockSupabaseConfig(page);
    await mockExistingSession(page);
    await page.goto(`/checkout?plan=${plan}`);

    await expect(page.locator('#payment-section')).toBeVisible({ timeout: 6000 });
  });

  test(`[${plan}] no session → payment form immediately visible`, async ({ page }) => {
    await mockSupabaseConfig(page);
    await mockNoSession(page);
    await page.goto(`/checkout?plan=${plan}`);

    await expect(page.locator('#payment-section')).toBeVisible({ timeout: 4000 });
    await expect(page.locator('#payment-element')).toBeAttached();
  });
}

// ─── /api/payment-intent endpoint ────────────────────────────────────────────

test('POST /api/payment-intent free plan returns setup_mode (age verification)', async ({ request }) => {
  const res = await request.post('/api/payment-intent', {
    data: JSON.stringify({ plan: 'free', email: 'test@example.com' }),
    headers: { 'Content-Type': 'application/json' },
  });
  // Free plan creates a SetupIntent for age verification — must not 500
  expect(res.status()).toBeLessThan(500);
  if (res.status() === 200) {
    const body = await res.json();
    // Either setup_mode (success) or an error from Stripe (unconfigured env) — both valid
    expect('setup_mode' in body || 'client_secret' in body || 'error' in body).toBeTruthy();
    // Must NOT return the old no_payment_required flag
    expect(body.no_payment_required).toBeFalsy();
  }
});

test('POST /api/payment-intent professional returns client_secret or config error (not 500)', async ({ request }) => {
  const res = await request.post('/api/payment-intent', {
    data: JSON.stringify({ plan: 'professional', email: 'test@example.com', name: 'Test User' }),
    headers: { 'Content-Type': 'application/json' },
  });
  expect(res.status()).toBeLessThan(500);
  if (res.status() === 200) {
    const body = await res.json();
    expect('client_secret' in body || 'error' in body || 'setup_mode' in body).toBeTruthy();
  }
});

test('POST /api/payment-intent founding returns client_secret or config error (not 500)', async ({ request }) => {
  const res = await request.post('/api/payment-intent', {
    data: JSON.stringify({ plan: 'founding', email: 'test@example.com', name: 'Test User' }),
    headers: { 'Content-Type': 'application/json' },
  });
  expect(res.status()).toBeLessThan(500);
});

test('POST /api/payment-intent empty body returns 4xx or config error (not 500)', async ({ request }) => {
  const res = await request.post('/api/payment-intent', { data: {} });
  expect(res.status()).toBeLessThan(500);
});

// ─── /api/supabase-config CORS ────────────────────────────────────────────────

test('GET /api/supabase-config with allowed origin returns url + anon_key', async ({ request }) => {
  const res = await request.get('/api/supabase-config', {
    headers: { Origin: 'https://neurontechnologies.ai' },
  });
  expect(res.status()).toBe(200);
  const body = await res.json();
  expect(body).toHaveProperty('url');
  expect(body).toHaveProperty('anon_key');
  expect(body.url).toMatch(/supabase/);
});

test('GET /api/supabase-config with disallowed origin returns 403', async ({ request }) => {
  const res = await request.get('/api/supabase-config', {
    headers: { Origin: 'https://evil-attacker.com' },
  });
  expect(res.status()).toBe(403);
});

// ─── Edge cases ───────────────────────────────────────────────────────────────

test('[unknown plan] defaults gracefully — 200 and non-empty body', async ({ page }) => {
  const res = await page.goto('/checkout?plan=unknown');
  expect(res?.status()).toBe(200);
  const body = (await page.locator('body').textContent()) ?? '';
  expect(body.trim().length).toBeGreaterThan(100);
});

test('[no plan param] checkout loads without error', async ({ page }) => {
  const res = await page.goto('/checkout');
  expect(res?.status()).toBe(200);
  await expect(page.locator('body')).not.toBeEmpty();
});

test('[checkout] page has no JS console errors on load (professional)', async ({ page }) => {
  const errors: string[] = [];
  page.on('console', (msg) => {
    if (msg.type() === 'error') errors.push(msg.text());
  });
  page.on('pageerror', (err) => errors.push(err.message));
  await page.goto('/checkout?plan=professional');
  await page.waitForTimeout(2000);
  // Filter out known third-party noise (Stripe, Supabase unreachable in test env)
  const criticalErrors = errors.filter(
    (e) =>
      !e.includes('stripe') &&
      !e.includes('Stripe') &&
      !e.includes('supabase') &&
      !e.includes('Failed to fetch') &&
      !e.includes('net::ERR') &&
      !e.includes('Content Security Policy')
  );
  expect(criticalErrors).toHaveLength(0);
});

test('[checkout] page has no JS console errors on load (free)', async ({ page }) => {
  const errors: string[] = [];
  page.on('console', (msg) => {
    if (msg.type() === 'error') errors.push(msg.text());
  });
  page.on('pageerror', (err) => errors.push(err.message));
  await page.goto('/checkout?plan=free');
  await page.waitForTimeout(2000);
  const criticalErrors = errors.filter(
    (e) =>
      !e.includes('stripe') &&
      !e.includes('Stripe') &&
      !e.includes('supabase') &&
      !e.includes('Failed to fetch') &&
      !e.includes('net::ERR') &&
      !e.includes('Content Security Policy')
  );
  expect(criticalErrors).toHaveLength(0);
});
