import { expect, test } from '@playwright/test';
import { Wallet } from 'ethers';
import { createWalletContext } from '../../setup/wallet-provider';
import { getStackConfig, validateStackHealthy } from '../../setup/stack';
import {
  createPersonaFeedback,
  addFeedbackStep,
  writePersonaFeedback,
  mintEth,
  buyKrk,
  resetChainState,
  connectWallet,
  type PersonaFeedback,
} from './helpers';
import { mkdirSync } from 'fs';
import { join } from 'path';

const STACK_CONFIG = getStackConfig();
const STACK_RPC_URL = STACK_CONFIG.rpcUrl;
const STACK_WEBAPP_URL = STACK_CONFIG.webAppUrl;
const STAKE_PAGE_URL = 'http://localhost:5173/stake';

// Different accounts for different personas
const MARCUS_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'; // Anvil #0
const SARAH_KEY = '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'; // Anvil #1
const PRIYA_KEY = '0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a'; // Anvil #2

test.describe('Test B: Staker Journey', () => {
  test.beforeAll(async () => {
    await resetChainState(STACK_RPC_URL);
    await validateStackHealthy(STACK_CONFIG);
  });

  test.describe.serial('Marcus - Degen/MEV ("where\'s the edge?")', () => {
    let feedback: PersonaFeedback;
    const accountKey = MARCUS_KEY;
    const accountAddr = new Wallet(accountKey).address;

    test.beforeAll(() => {
      feedback = createPersonaFeedback('marcus', 'B', 'staker');
    });

    test('Marcus pre-funds wallet with KRK', async ({ browser }) => {
      const context = await createWalletContext(browser, {
        privateKey: accountKey,
        rpcUrl: STACK_RPC_URL,
      });

      const page = await context.newPage();
      const observations: string[] = [];

      try {
        await page.goto(`${STACK_WEBAPP_URL}/app/`, { waitUntil: 'domcontentloaded' });
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(2_000);

        observations.push('Setting up: acquiring KRK for staking tests...');
        await mintEth(page, STACK_RPC_URL, accountAddr, '50');
        await buyKrk(page, '10', STACK_RPC_URL, accountKey);
        observations.push('✓ Wallet funded with KRK');

        addFeedbackStep(feedback, 'setup', observations);

      } finally {
        await context.close();
      }
    });

    test('Marcus analyzes staking interface for MEV opportunities', async ({ browser }) => {
      const context = await createWalletContext(browser, {
        privateKey: accountKey,
        rpcUrl: STACK_RPC_URL,
      });

      const page = await context.newPage();
      page.on('console', msg => console.log(`[BROWSER] ${msg.type()}: ${msg.text()}`));
      const observations: string[] = [];

      try {
        await page.goto(STAKE_PAGE_URL, { waitUntil: 'domcontentloaded' });
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(3_000);

        observations.push('Scanning for arbitrage angles, tax rate gaps, snatching opportunities...');

        // Check if leverage framing is clear
        const hasLeverageInfo = await page.getByText(/leverage|multiplier|amplif/i).isVisible().catch(() => false);
        if (hasLeverageInfo) {
          observations.push('✓ Leverage mechanics visible - can assess risk/reward multiplier');
        } else {
          observations.push('⚠ Leverage framing unclear - hard to calculate edge');
          feedback.overall.friction.push('Leverage mechanics not clearly explained');
        }

        // Tax rate tooltip
        const taxSelect = page.getByRole('combobox', { name: /tax/i }).first();
        const taxVisible = await taxSelect.isVisible({ timeout: 5_000 }).catch(() => false);
        
        if (taxVisible) {
          observations.push('✓ Tax rate selector found');
          
          // Look for tooltip or info icon
          const infoIcon = page.locator('svg[data-icon="circle-info"], svg[class*="info"]').first();
          const hasTooltip = await infoIcon.isVisible().catch(() => false);
          
          if (hasTooltip) {
            observations.push('✓ Tax rate has tooltip - explains tradeoff');
            await infoIcon.hover();
            // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
            await page.waitForTimeout(1_000);
            
            const tooltipText = await page.locator('[role="tooltip"], .tooltip').textContent().catch(() => '');
            if (tooltipText.toLowerCase().includes('snatch') || tooltipText.toLowerCase().includes('harder')) {
              observations.push('✓ Tooltip explains "higher tax = harder to snatch" - good framing');
            } else {
              observations.push('⚠ Tooltip doesn\'t clearly explain snatch resistance');
            }
          } else {
            observations.push('✗ No tooltip on tax rate - mechanics unclear');
            feedback.overall.friction.push('Tax rate selection lacks explanation');
          }
        } else {
          observations.push('✗ Tax rate selector not found');
        }

        // Protocol stats visibility
        const hasStats = await page.locator('text=/TVL|total staked|positions/i').isVisible().catch(() => false);
        if (hasStats) {
          observations.push('✓ Protocol stats visible - can gauge competition and pool depth');
        } else {
          observations.push('⚠ No protocol-wide stats - harder to assess meta');
        }

        // Contract addresses for verification
        const hasContracts = await page.locator('text=/0x[a-fA-F0-9]{40}|contract/i').isVisible().catch(() => false);
        if (hasContracts) {
          observations.push('✓ Contract addresses visible - can verify on-chain before committing');
        } else {
          observations.push('✗ No contract addresses shown - can\'t independently verify');
          feedback.overall.friction.push('No contract addresses for verification');
        }

        // Look for open positions to snatch
        const positionsList = await page.locator('[class*="position"], [class*="stake-card"]').count();
        if (positionsList > 0) {
          observations.push(`✓ Can see ${positionsList} existing positions - potential snatch targets`);
        } else {
          observations.push('⚠ Can\'t see other stakers\' positions - no snatching meta visible');
        }

        // Screenshot
        const screenshotDir = join('test-results', 'usertest', 'marcus-b');
        mkdirSync(screenshotDir, { recursive: true });
        const screenshotPath = join(screenshotDir, `stake-interface-analysis-${Date.now()}.png`);
        await page.screenshot({ path: screenshotPath, fullPage: true });

        addFeedbackStep(feedback, 'stake-interface-analysis', observations, screenshotPath);

      } finally {
        await context.close();
      }
    });

    test('Marcus executes aggressive low-tax stake', async ({ browser }) => {
      const context = await createWalletContext(browser, {
        privateKey: accountKey,
        rpcUrl: STACK_RPC_URL,
      });

      const page = await context.newPage();
      const observations: string[] = [];

      try {
        await page.goto(STAKE_PAGE_URL, { waitUntil: 'domcontentloaded' });
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(3_000);

        // Connect wallet
        await connectWallet(page);
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(2_000);

        observations.push('Going for lowest tax rate - maximum upside, I\'ll just monitor for snatches');

        // Fill stake form
        const stakeAmountInput = page.getByLabel(/staking amount/i).or(page.locator('input[type="number"]').first());
        await stakeAmountInput.waitFor({ state: 'visible', timeout: 10_000 });
        await stakeAmountInput.fill('100');
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(500);

        // Select lowest tax rate (index 0 or value "5")
        const taxSelect = page.getByRole('combobox', { name: /tax/i }).first();
        await taxSelect.selectOption({ index: 0 }); // Pick first option (lowest)
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(500);

        const selectedTax = await taxSelect.inputValue();
        observations.push(`Selected tax rate: ${selectedTax}% (lowest available)`);

        // Screenshot before stake
        const screenshotDir = join('test-results', 'usertest', 'marcus-b');
        const preStakePath = join(screenshotDir, `pre-stake-${Date.now()}.png`);
        await page.screenshot({ path: preStakePath, fullPage: true });

        // Execute stake
        const stakeButton = page.getByRole('button', { name: /^(stake|snatch)/i }).first();
        const buttonText = await stakeButton.textContent();
        
        if (buttonText?.toLowerCase().includes('snatch')) {
          observations.push('✓ Button shows "Snatch and Stake" - clear that I\'m taking someone\'s position');
        } else {
          observations.push('Button shows "Stake" - am I creating new position or snatching?');
        }

        try {
          await stakeButton.click();
          // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
          await page.waitForTimeout(1_000);
          
          // Wait for transaction
          const txInProgress = await page.getByRole('button', { name: /sign|waiting|confirm/i }).isVisible({ timeout: 3_000 }).catch(() => false);
          if (txInProgress) {
            // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
            await page.waitForTimeout(5_000);
          }
          
          observations.push('✓ Stake transaction executed');
        } catch (error: any) {
          observations.push(`✗ Stake failed: ${error.message}`);
          feedback.overall.friction.push('Could not complete stake transaction');
        }

        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(3_000);

        // Screenshot after stake
        const postStakePath = join(screenshotDir, `post-stake-${Date.now()}.png`);
        await page.screenshot({ path: postStakePath, fullPage: true });

        addFeedbackStep(feedback, 'execute-stake', observations, postStakePath);

      } finally {
        await context.close();
      }
    });

    test('Marcus checks position P&L and monitoring tools', async ({ browser }) => {
      const context = await createWalletContext(browser, {
        privateKey: accountKey,
        rpcUrl: STACK_RPC_URL,
      });

      const page = await context.newPage();
      const observations: string[] = [];

      try {
        await page.goto(STAKE_PAGE_URL, { waitUntil: 'domcontentloaded' });
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(3_000);

        await connectWallet(page);
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(2_000);

        observations.push('Checking my position - where\'s the P&L?');

        // Look for position card/details
        const hasPositionCard = await page.locator('[class*="position"], [class*="your-stake"]').isVisible().catch(() => false);
        if (hasPositionCard) {
          observations.push('✓ Position card visible');
        } else {
          observations.push('⚠ Can\'t find my position display');
        }

        // P&L visibility
        const hasPnL = await page.locator('text=/profit|loss|P&L|gain|\\+\\$|\\-\\$/i').isVisible().catch(() => false);
        if (hasPnL) {
          observations.push('✓ P&L displayed - can see if I\'m winning');
        } else {
          observations.push('✗ No P&L shown - can\'t tell if this is profitable');
          feedback.overall.friction.push('Position P&L not visible');
        }

        // Tax accumulation / time held
        const hasTimeMetrics = await page.locator('text=/time|duration|days|hours/i').isVisible().catch(() => false);
        if (hasTimeMetrics) {
          observations.push('✓ Time-based metrics shown - can calculate tax accumulation');
        } else {
          observations.push('⚠ No time held display - harder to estimate when I\'ll be profitable');
        }

        // Snatch risk indicator
        const hasSnatchRisk = await page.locator('text=/snatch risk|vulnerable|safe/i').isVisible().catch(() => false);
        if (hasSnatchRisk) {
          observations.push('✓ Snatch risk indicator - helps me decide when to exit');
        } else {
          observations.push('⚠ No snatch risk metric - flying blind on when I\'ll get snatched');
        }

        // Next steps clarity
        const hasActions = await page.getByRole('button', { name: /claim|exit|increase/i }).isVisible().catch(() => false);
        if (hasActions) {
          observations.push('✓ Clear action buttons - know what I can do next');
        } else {
          observations.push('⚠ Not clear what actions I can take with this position');
        }

        // Screenshot
        const screenshotDir = join('test-results', 'usertest', 'marcus-b');
        const screenshotPath = join(screenshotDir, `position-monitoring-${Date.now()}.png`);
        await page.screenshot({ path: screenshotPath, fullPage: true });

        addFeedbackStep(feedback, 'position-monitoring', observations, screenshotPath);

        // Marcus's verdict
        const hasEdge = observations.some(o => o.includes('✓ P&L displayed'));
        const canMonitor = hasPnL || hasSnatchRisk;

        feedback.overall.wouldStake = true; // Marcus is a degen, he'll stake anyway
        feedback.overall.wouldReturn = canMonitor;

        observations.push(`Marcus verdict: ${hasEdge ? 'Clear edge, will monitor actively' : 'Can\'t calculate edge properly'}`);
        observations.push(`Would return: ${canMonitor ? 'Yes, need to watch for snatches' : 'Maybe, but tooling is weak'}`);

      } finally {
        await context.close();
        writePersonaFeedback(feedback);
      }
    });
  });

  test.describe.serial('Sarah - Yield Farmer ("what are the risks?")', () => {
    let feedback: PersonaFeedback;
    const accountKey = SARAH_KEY;
    const accountAddr = new Wallet(accountKey).address;

    test.beforeAll(() => {
      feedback = createPersonaFeedback('sarah', 'B', 'staker');
    });

    test('Sarah pre-funds wallet with KRK', async ({ browser }) => {
      const context = await createWalletContext(browser, {
        privateKey: accountKey,
        rpcUrl: STACK_RPC_URL,
      });

      const page = await context.newPage();
      const observations: string[] = [];

      try {
        await page.goto(`${STACK_WEBAPP_URL}/app/`, { waitUntil: 'domcontentloaded' });
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(2_000);

        observations.push('Funding wallet for conservative staking test...');
        await mintEth(page, STACK_RPC_URL, accountAddr, '50');
        await buyKrk(page, '10', STACK_RPC_URL, accountKey);
        observations.push('✓ Wallet funded');

        addFeedbackStep(feedback, 'setup', observations);

      } finally {
        await context.close();
      }
    });

    test('Sarah evaluates risk disclosure and staking mechanics', async ({ browser }) => {
      const context = await createWalletContext(browser, {
        privateKey: accountKey,
        rpcUrl: STACK_RPC_URL,
      });

      const page = await context.newPage();
      const observations: string[] = [];

      try {
        await page.goto(STAKE_PAGE_URL, { waitUntil: 'domcontentloaded' });
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(3_000);

        observations.push('Looking for risk disclosures, worst-case scenarios, and safety features...');

        // Risk warnings
        const hasRiskWarning = await page.getByText(/risk|warning|caution|loss/i).isVisible().catch(() => false);
        if (hasRiskWarning) {
          observations.push('✓ Risk warning present - shows responsible disclosure');
        } else {
          observations.push('✗ No visible risk warnings - concerning for risk management');
          feedback.overall.friction.push('No risk disclosure on staking interface');
        }

        // Tax rate explanation with safety framing
        const taxTooltipFound = await page.locator('svg[data-icon="circle-info"], svg[class*="info"]').first().isVisible().catch(() => false);
        if (taxTooltipFound) {
          observations.push('✓ Tax rate info icon found');
          
          await page.locator('svg[data-icon="circle-info"], svg[class*="info"]').first().hover();
          // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
          await page.waitForTimeout(1_000);
          
          const tooltipText = await page.locator('[role="tooltip"], .tooltip').textContent().catch(() => '');
          if (tooltipText.toLowerCase().includes('reduce') || tooltipText.toLowerCase().includes('return')) {
            observations.push('✓ Tooltip explains tax impact on returns - good risk education');
          } else {
            observations.push('⚠ Tooltip doesn\'t clearly explain how tax affects my returns');
          }
        } else {
          observations.push('✗ No tooltip on tax rate - critical mechanism unexplained');
          feedback.overall.friction.push('Tax rate mechanism not explained');
        }

        // Protocol stats for safety assessment
        const hasProtocolStats = await page.locator('text=/TVL|health|utilization/i').isVisible().catch(() => false);
        if (hasProtocolStats) {
          observations.push('✓ Protocol stats visible - can assess overall protocol health');
        } else {
          observations.push('⚠ No protocol health stats - hard to assess systemic risk');
        }

        // APY/yield projections
        const hasAPY = await page.locator('text=/APY|yield|return|%/i').isVisible().catch(() => false);
        if (hasAPY) {
          observations.push('✓ Yield projections visible - can compare to other protocols');
        } else {
          observations.push('⚠ No clear APY display - can\'t evaluate if returns justify risk');
          feedback.overall.friction.push('No yield projections shown');
        }

        // Smart contract verification
        const hasContractInfo = await page.locator('text=/0x[a-fA-F0-9]{40}|verified|audit/i').isVisible().catch(() => false);
        if (hasContractInfo) {
          observations.push('✓ Contract info or audit badge visible - can verify safety');
        } else {
          observations.push('⚠ No contract verification info - can\'t independently audit');
        }

        // Screenshot
        const screenshotDir = join('test-results', 'usertest', 'sarah-b');
        mkdirSync(screenshotDir, { recursive: true });
        const screenshotPath = join(screenshotDir, `risk-assessment-${Date.now()}.png`);
        await page.screenshot({ path: screenshotPath, fullPage: true });

        addFeedbackStep(feedback, 'risk-assessment', observations, screenshotPath);

      } finally {
        await context.close();
      }
    });

    test('Sarah executes conservative stake with medium tax rate', async ({ browser }) => {
      const context = await createWalletContext(browser, {
        privateKey: accountKey,
        rpcUrl: STACK_RPC_URL,
      });

      const page = await context.newPage();
      const observations: string[] = [];

      try {
        await page.goto(STAKE_PAGE_URL, { waitUntil: 'domcontentloaded' });
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(3_000);

        await connectWallet(page);
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(2_000);

        observations.push('Choosing medium tax rate - balance between returns and safety');

        // Fill stake form
        const stakeAmountInput = page.getByLabel(/staking amount/i).or(page.locator('input[type="number"]').first());
        await stakeAmountInput.waitFor({ state: 'visible', timeout: 10_000 });
        await stakeAmountInput.fill('50'); // Conservative amount
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(500);

        // Select medium tax rate (index 2-3, or 10-15%)
        const taxSelect = page.getByRole('combobox', { name: /tax/i }).first();
        const options = await taxSelect.locator('option').count();
        const midIndex = Math.floor(options / 2);
        await taxSelect.selectOption({ index: midIndex });
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(500);

        const selectedTax = await taxSelect.inputValue();
        observations.push(`Selected tax rate: ${selectedTax}% (medium - balanced risk/reward)`);

        // Screenshot before stake
        const screenshotDir = join('test-results', 'usertest', 'sarah-b');
        const preStakePath = join(screenshotDir, `pre-stake-${Date.now()}.png`);
        await page.screenshot({ path: preStakePath, fullPage: true });

        // Execute stake
        const stakeButton = page.getByRole('button', { name: /^(stake|snatch)/i }).first();
        
        try {
          await stakeButton.click();
          // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
          await page.waitForTimeout(1_000);
          
          const txInProgress = await page.getByRole('button', { name: /sign|waiting|confirm/i }).isVisible({ timeout: 3_000 }).catch(() => false);
          if (txInProgress) {
            // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
            await page.waitForTimeout(5_000);
          }
          
          observations.push('✓ Conservative stake executed');
        } catch (error: any) {
          observations.push(`✗ Stake failed: ${error.message}`);
          feedback.overall.friction.push('Stake transaction failed');
        }

        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(3_000);

        const postStakePath = join(screenshotDir, `post-stake-${Date.now()}.png`);
        await page.screenshot({ path: postStakePath, fullPage: true });

        addFeedbackStep(feedback, 'execute-stake', observations, postStakePath);

      } finally {
        await context.close();
      }
    });

    test('Sarah evaluates post-stake clarity and monitoring', async ({ browser }) => {
      const context = await createWalletContext(browser, {
        privateKey: accountKey,
        rpcUrl: STACK_RPC_URL,
      });

      const page = await context.newPage();
      const observations: string[] = [];

      try {
        await page.goto(STAKE_PAGE_URL, { waitUntil: 'domcontentloaded' });
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(3_000);

        await connectWallet(page);
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(2_000);

        observations.push('Evaluating: Can I clearly see my position, returns, and risks?');

        // Position visibility
        const hasPosition = await page.locator('[class*="position"], [class*="stake"]').isVisible().catch(() => false);
        if (hasPosition) {
          observations.push('✓ Position card visible');
        } else {
          observations.push('⚠ Position not clearly displayed');
        }

        // Expected returns
        const hasReturns = await page.locator('text=/daily|weekly|APY|earning/i').isVisible().catch(() => false);
        if (hasReturns) {
          observations.push('✓ Return projections visible - know what to expect');
        } else {
          observations.push('⚠ No clear return projections - don\'t know expected earnings');
          feedback.overall.friction.push('No return projections for active positions');
        }

        // What happens next
        const hasGuidance = await page.getByText(/next|monitor|check back|claim/i).isVisible().catch(() => false);
        if (hasGuidance) {
          observations.push('✓ Guidance on next steps - know when to check back');
        } else {
          observations.push('⚠ No guidance on what happens next - set and forget?');
        }

        // Exit options
        const hasExit = await page.getByRole('button', { name: /unstake|exit|withdraw/i }).isVisible().catch(() => false);
        if (hasExit) {
          observations.push('✓ Exit option visible - not locked in permanently');
        } else {
          observations.push('⚠ No clear exit option - am I stuck until snatched?');
          feedback.overall.friction.push('Exit mechanism not clear');
        }

        // Screenshot
        const screenshotDir = join('test-results', 'usertest', 'sarah-b');
        const screenshotPath = join(screenshotDir, `post-stake-clarity-${Date.now()}.png`);
        await page.screenshot({ path: screenshotPath, fullPage: true });

        addFeedbackStep(feedback, 'post-stake-clarity', observations, screenshotPath);

        // Sarah's verdict
        const risksExplained = observations.filter(o => o.includes('✓')).length >= 3;
        const canMonitor = hasReturns || hasPosition;

        feedback.overall.wouldStake = risksExplained;
        feedback.overall.wouldReturn = canMonitor;

        observations.push(`Sarah verdict: ${risksExplained ? 'Acceptable risk profile' : 'Too many unknowns, won\'t stake'}`);
        observations.push(`Would return: ${canMonitor ? 'Yes, to monitor position' : 'Unclear monitoring requirements'}`);

      } finally {
        await context.close();
        writePersonaFeedback(feedback);
      }
    });
  });

  test.describe.serial('Priya - Institutional ("show me the docs")', () => {
    let feedback: PersonaFeedback;
    const accountKey = PRIYA_KEY;
    const accountAddr = new Wallet(accountKey).address;

    test.beforeAll(() => {
      feedback = createPersonaFeedback('priya', 'B', 'staker');
    });

    test('Priya pre-funds wallet with KRK', async ({ browser }) => {
      const context = await createWalletContext(browser, {
        privateKey: accountKey,
        rpcUrl: STACK_RPC_URL,
      });

      const page = await context.newPage();
      const observations: string[] = [];

      try {
        await page.goto(`${STACK_WEBAPP_URL}/app/`, { waitUntil: 'domcontentloaded' });
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(2_000);

        observations.push('Preparing test wallet...');
        await mintEth(page, STACK_RPC_URL, accountAddr, '50');
        await buyKrk(page, '10', STACK_RPC_URL, accountKey);
        observations.push('✓ Wallet funded');

        addFeedbackStep(feedback, 'setup', observations);

      } finally {
        await context.close();
      }
    });

    test('Priya audits documentation and contract transparency', async ({ browser }) => {
      const context = await createWalletContext(browser, {
        privateKey: accountKey,
        rpcUrl: STACK_RPC_URL,
      });

      const page = await context.newPage();
      const observations: string[] = [];

      try {
        await page.goto(STAKE_PAGE_URL, { waitUntil: 'domcontentloaded' });
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(3_000);

        observations.push('Looking for: docs, contract addresses, audit reports, technical specs...');

        // Documentation link
        const hasDocsLink = await page.locator('a[href*="docs"], a[href*="documentation"]').or(page.getByText(/documentation|whitepaper|docs/i)).isVisible().catch(() => false);
        if (hasDocsLink) {
          observations.push('✓ Documentation link visible - can review technical details');
        } else {
          observations.push('✗ No documentation link - cannot perform due diligence');
          feedback.overall.friction.push('No technical documentation accessible');
        }

        // Contract addresses with copy button
        const contractAddresses = await page.locator('text=/0x[a-fA-F0-9]{40}/').count();
        if (contractAddresses > 0) {
          observations.push(`✓ Found ${contractAddresses} contract address(es) - can verify on Etherscan`);
          
          const hasCopyButton = await page.locator('button[title*="copy"], button[aria-label*="copy"]').isVisible().catch(() => false);
          if (hasCopyButton) {
            observations.push('✓ Copy button for addresses - good UX for verification');
          } else {
            observations.push('⚠ No copy button - minor friction for address verification');
          }
        } else {
          observations.push('✗ No contract addresses visible - cannot verify on-chain');
          feedback.overall.friction.push('Contract addresses not displayed');
        }

        // Audit badge or report
        const hasAudit = await page.locator('a[href*="audit"]').or(page.getByText(/audited|security audit/i)).isVisible().catch(() => false);
        if (hasAudit) {
          observations.push('✓ Audit report accessible - critical for institutional review');
        } else {
          observations.push('✗ No audit report linked - major blocker for institutional capital');
          feedback.overall.friction.push('No audit report accessible from UI');
        }

        // Protocol parameters visibility
        const hasParams = await page.locator('text=/parameter|config|setting/i').isVisible().catch(() => false);
        if (hasParams) {
          observations.push('✓ Protocol parameters visible - can assess mechanism design');
        } else {
          observations.push('⚠ Protocol parameters not displayed - harder to model behavior');
        }

        // GitHub or source code link
        const hasGitHub = await page.locator('a[href*="github"]').isVisible().catch(() => false);
        if (hasGitHub) {
          observations.push('✓ GitHub link present - can review source code');
        } else {
          observations.push('⚠ No source code link - cannot independently verify implementation');
        }

        // Screenshot
        const screenshotDir = join('test-results', 'usertest', 'priya-b');
        mkdirSync(screenshotDir, { recursive: true });
        const screenshotPath = join(screenshotDir, `documentation-audit-${Date.now()}.png`);
        await page.screenshot({ path: screenshotPath, fullPage: true });

        addFeedbackStep(feedback, 'documentation-audit', observations, screenshotPath);

      } finally {
        await context.close();
      }
    });

    test('Priya evaluates UI professionalism and data quality', async ({ browser }) => {
      const context = await createWalletContext(browser, {
        privateKey: accountKey,
        rpcUrl: STACK_RPC_URL,
      });

      const page = await context.newPage();
      const observations: string[] = [];

      try {
        await page.goto(STAKE_PAGE_URL, { waitUntil: 'domcontentloaded' });
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(3_000);

        await connectWallet(page);
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(2_000);

        observations.push('Evaluating UI quality: precision, accuracy, professionalism...');

        // Numeric precision
        const numbers = await page.locator('text=/\\d+\\.\\d{2,}/').count();
        if (numbers > 2) {
          observations.push(`✓ Found ${numbers} precise numbers - shows data quality`);
        } else {
          observations.push('⚠ Limited numeric precision - data may be rounded/imprecise');
        }

        // Real-time data indicators
        const hasLiveData = await page.locator('text=/live|real-time|updated/i').isVisible().catch(() => false);
        if (hasLiveData) {
          observations.push('✓ Real-time data indicators - shows active monitoring');
        } else {
          observations.push('⚠ No indication if data is live or stale');
        }

        // Error states and edge cases
        observations.push('Testing edge cases: trying to stake 0...');
        const stakeInput = page.getByLabel(/staking amount/i).or(page.locator('input[type="number"]').first());
        await stakeInput.fill('0');
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(500);

        const hasValidation = await page.locator('text=/invalid|minimum|required/i').isVisible().catch(() => false);
        if (hasValidation) {
          observations.push('✓ Input validation present - handles edge cases gracefully');
        } else {
          observations.push('⚠ No visible validation for invalid inputs');
        }

        // Clear labels and units
        const hasUnits = await page.locator('text=/KRK|ETH|%|USD/i').count();
        if (hasUnits >= 3) {
          observations.push('✓ Clear units on all values - professional data presentation');
        } else {
          observations.push('⚠ Some values missing units - could cause confusion');
        }

        // Screenshot
        const screenshotDir = join('test-results', 'usertest', 'priya-b');
        const screenshotPath = join(screenshotDir, `ui-quality-${Date.now()}.png`);
        await page.screenshot({ path: screenshotPath, fullPage: true });

        addFeedbackStep(feedback, 'ui-quality', observations, screenshotPath);

      } finally {
        await context.close();
      }
    });

    test('Priya performs test stake and evaluates reporting', async ({ browser }) => {
      const context = await createWalletContext(browser, {
        privateKey: accountKey,
        rpcUrl: STACK_RPC_URL,
      });

      const page = await context.newPage();
      const observations: string[] = [];

      try {
        await page.goto(STAKE_PAGE_URL, { waitUntil: 'domcontentloaded' });
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(3_000);

        await connectWallet(page);
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(2_000);

        observations.push('Executing small test stake to evaluate position reporting...');

        // Fill form
        const stakeInput = page.getByLabel(/staking amount/i).or(page.locator('input[type="number"]').first());
        await stakeInput.fill('25');
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(500);

        const taxSelect = page.getByRole('combobox', { name: /tax/i }).first();
        await taxSelect.selectOption({ index: 1 });
        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(500);

        // Execute
        const stakeButton = page.getByRole('button', { name: /^(stake|snatch)/i }).first();
        
        try {
          await stakeButton.click();
          // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
          await page.waitForTimeout(1_000);
          
          const txInProgress = await page.getByRole('button', { name: /sign|waiting|confirm/i }).isVisible({ timeout: 3_000 }).catch(() => false);
          if (txInProgress) {
            // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
            await page.waitForTimeout(5_000);
          }
          
          observations.push('✓ Test stake executed');
        } catch (error: any) {
          observations.push(`✗ Stake failed: ${error.message}`);
        }

        // eslint-disable-next-line no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles.
        await page.waitForTimeout(3_000);

        // Evaluate position reporting
        observations.push('Checking position dashboard for institutional-grade reporting...');

        // Transaction hash
        const hasTxHash = await page.locator('text=/0x[a-fA-F0-9]{64}|transaction|tx/i').isVisible().catch(() => false);
        if (hasTxHash) {
          observations.push('✓ Transaction hash visible - can verify on Etherscan');
        } else {
          observations.push('⚠ No transaction hash shown - harder to verify on-chain');
        }

        // Position details
        const hasDetails = await page.locator('text=/amount|tax rate|time|date/i').count();
        if (hasDetails >= 3) {
          observations.push('✓ Comprehensive position details - sufficient for reporting');
        } else {
          observations.push('⚠ Limited position details - insufficient for audit trail');
        }

        // Export or reporting tools
        const hasExport = await page.getByRole('button', { name: /export|download|csv/i }).isVisible().catch(() => false);
        if (hasExport) {
          observations.push('✓ Export functionality - can generate reports for compliance');
        } else {
          observations.push('✗ No export option - manual record-keeping required');
          feedback.overall.friction.push('No position export for institutional reporting');
        }

        // Screenshot
        const screenshotDir = join('test-results', 'usertest', 'priya-b');
        const screenshotPath = join(screenshotDir, `position-reporting-${Date.now()}.png`);
        await page.screenshot({ path: screenshotPath, fullPage: true });

        addFeedbackStep(feedback, 'position-reporting', observations, screenshotPath);

        // Priya's verdict
        const hasRequiredDocs = observations.filter(o => o.includes('✓')).length >= 4;
        const meetsStandards = !observations.some(o => o.includes('✗ No audit report'));

        feedback.overall.wouldStake = hasRequiredDocs && meetsStandards;
        feedback.overall.wouldReturn = hasRequiredDocs;

        observations.push(`Priya verdict: ${feedback.overall.wouldStake ? 'Meets institutional standards' : 'Insufficient documentation/transparency'}`);
        observations.push(`Would recommend: ${meetsStandards ? 'Yes, with caveats' : 'No, needs audit and better docs'}`);

      } finally {
        await context.close();
        writePersonaFeedback(feedback);
      }
    });
  });
});
