/**
 * Browser QA for Layer 10 returns. Drives the real running stack:
 *   - alqove-api on http://localhost:8000 (with STRIPE_FAKE_REFUNDS=1)
 *   - Next dev server on http://localhost:3000
 *   - Mailpit on http://localhost:8025
 *
 * Fixture (token + order id) is read from /tmp/qa-fixture.json which is
 * produced by the seed script in alqove-api.
 *
 * Run with:
 *   cd web && PORT=3000 MOCK_API_PORT=8000 npx playwright test --project=chromium e2e/qa-returns.spec.ts --headed
 */

import { test, expect, Page } from '@playwright/test';
import { readFileSync } from 'node:fs';

const fixture = JSON.parse(readFileSync('/tmp/qa-fixture.json', 'utf8')) as {
  purchase_id: string;
  order_id: string;
  buyer_token: string;
  seller_token: string;
};

const WEB = process.env.QA_WEB ?? 'http://localhost:3000';

async function authAs(page: Page, token: string) {
  // Inject the token via initScript so it's already in localStorage before
  // AuthBootstrap mounts. Navigating between pages after a setItem races
  // against the in-flight /v1/auth/me — when that fetch is aborted by the
  // navigation, AuthStore's catch() removes the token, leaving the next
  // page unauthenticated.
  await page.addInitScript((t) => {
    try {
      window.localStorage.setItem('auth_token', t);
    } catch {}
  }, token);
}

test.describe.configure({ mode: 'serial' });

test.describe('Layer 10 returns — buyer + seller flow', () => {
  test('buyer sees Request return button on a delivered order in window', async ({ page }) => {
    await authAs(page, fixture.buyer_token);
    await page.goto(`${WEB}/purchases/${fixture.purchase_id}`);

    await expect(page.getByRole('heading', { name: /Purchase Details/i })).toBeVisible();
    await expect(
      page.getByRole('button', { name: /Request return/i }),
    ).toBeVisible({ timeout: 10_000 });

    await page.screenshot({ path: '/tmp/qa-1-request-button.png', fullPage: true });
  });

  test('buyer files a return for the damaged item', async ({ page }) => {
    await authAs(page, fixture.buyer_token);
    await page.goto(`${WEB}/purchases/${fixture.purchase_id}`);

    await page.getByRole('button', { name: /Request return/i }).click();
    await expect(page.getByRole('dialog', { name: /Request a return/i })).toBeVisible();

    await page.screenshot({ path: '/tmp/qa-2-modal-open.png' });

    // Pick "Damaged" and submit
    await page.getByLabel(/Item arrived damaged/i).click();
    await page.getByRole('button', { name: /^File return$/i }).click();

    // Modal should close, state strip should now show "Awaiting seller"
    await expect(page.getByText('Awaiting seller')).toBeVisible({ timeout: 10_000 });
    await page.screenshot({ path: '/tmp/qa-3-buyer-after-file.png', fullPage: true });
  });

  test('seller sees the new return in /seller/returns', async ({ page }) => {
    await authAs(page, fixture.seller_token);
    await page.goto(`${WEB}/seller/returns`);

    await expect(page.getByRole('heading', { name: /Returns/i })).toBeVisible();
    // Default chip is "Awaiting" (state=requested) — our return should appear
    await expect(page.getByText('Awaiting seller').first()).toBeVisible({
      timeout: 10_000,
    });

    await page.screenshot({ path: '/tmp/qa-4-seller-list.png', fullPage: true });
  });

  test('seller approves the return — refunded + closed within seconds', async ({ page }) => {
    await authAs(page, fixture.seller_token);
    await page.goto(`${WEB}/seller/orders/${fixture.order_id}`);

    await expect(page.getByRole('heading', { level: 1 })).toBeVisible();

    // Scroll the Return panel into view
    const approveBtn = page.getByRole('button', { name: /^Approve$/i });
    await approveBtn.scrollIntoViewIfNeeded();
    await expect(approveBtn).toBeVisible({ timeout: 10_000 });

    await page.screenshot({ path: '/tmp/qa-5-seller-panel.png', fullPage: true });

    await approveBtn.click();
    await page.getByRole('button', { name: /Confirm approve/i }).click();

    // After approve, transitioner runs through Approved → Refunded → Closed.
    // The Return panel's state badge should flip to Closed.
    const stateBadge = page.getByTestId('return-state-badge');
    await expect(stateBadge).toHaveAttribute('data-state', 'closed', { timeout: 15_000 });
    // Refund amount + system message should appear in the thread.
    await expect(
      page.getByText(/Refunded \$50\.00 to the original payment method/),
    ).toBeVisible();

    await page.screenshot({ path: '/tmp/qa-6-seller-after-approve.png', fullPage: true });
  });

  test('buyer purchase view shows Closed state strip and hides Request return', async ({ page }) => {
    await authAs(page, fixture.buyer_token);
    await page.goto(`${WEB}/purchases/${fixture.purchase_id}`, {
      waitUntil: 'networkidle',
    });

    // OrderResource exposes the latest return id regardless of state, so the
    // state strip stays visible after Plan 1's auto-close. Buyer cannot
    // refile on the same order — Request return must be gone.
    await expect(page.getByRole('button', { name: /Request return/i })).toHaveCount(0);
    await expect(page.getByTestId('return-state-badge')).toHaveAttribute(
      'data-state',
      'closed',
    );

    await page.screenshot({ path: '/tmp/qa-7-buyer-final.png', fullPage: true });
  });

  test('mailpit received the four notification emails', async ({ request }) => {
    const resp = await request.get('http://localhost:8025/api/v1/messages');
    expect(resp.ok()).toBeTruthy();
    const body = (await resp.json()) as { messages: { Subject: string; To: { Address: string }[] }[] };
    const subjects = body.messages.map((m) => m.Subject);
    // We expect at least: ReturnRequested (to seller), ReturnApproved + ReturnRefunded (to buyer)
    const matches = subjects.filter(
      (s) =>
        /New return request/i.test(s) ||
        /was approved/i.test(s) ||
        /refunded/i.test(s),
    );
    expect(matches.length).toBeGreaterThanOrEqual(2);
  });
});
