import { test, expect } from '../fixtures';
import type { Page } from '@playwright/test';
import { buildNoteUrl, defaultRandomSecret } from 'lnurlcash-kit';
import { MINT_ORIGIN, NOTE_PATH } from '../helpers/MintMocker';
import { createFreshWallet } from '../helpers/wallet';

// a 33-byte compressed secp256k1 pubkey, hex - what the trusted-mint
// registry accepts
const MINT_PUBKEY = `02${'ab'.repeat(32)}`;

// the discovery behind a one-tap suggestion: the app resolves
// "@mint.600.wtf" to https://mint.600.wtf/.well-known/lnurlp/mint, derives
// the mint-address endpoint (/.well-known/lnurlw/mint) and reads the
// signing key there - the payRequest deliberately does NOT carry
// mintPubkey (LUD-25 advertises it on the withdraw side only)
const mockSuggestionMint = async (page: Page): Promise<void> => {
  const fulfill = async (route: import('@playwright/test').Route, body: unknown) => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      headers: { 'Access-Control-Allow-Origin': '*' },
      body: JSON.stringify(body),
    });
  };
  await page.route(/^https:\/\/mint\.600\.wtf\/\.well-known\/lnurlw\/mint/, (route) =>
    fulfill(route, {
      tag: 'withdrawRequest',
      callback: 'https://mint.600.wtf/w',
      minWithdrawable: 1000,
      maxWithdrawable: 100_000_000_000,
      mintPubkey: MINT_PUBKEY,
      payLink: 'https://mint.600.wtf/.well-known/lnurlp/mint',
    }),
  );
  await page.route(/^https:\/\/mint\.600\.wtf\/\.well-known\/lnurlp\/mint/, (route) =>
    fulfill(route, {
      tag: 'payRequest',
      callback: 'https://mint.600.wtf/pay',
      minSendable: 1000,
      maxSendable: 100_000_000_000,
      withdrawLink: 'https://mint.600.wtf/note',
      metadata: '[]',
    }),
  );
};

test.describe('Manage mints', () => {
  test('trust a suggested mint, set it default, remove it', async ({ page }) => {
    await mockSuggestionMint(page);
    await createFreshWallet(page);

    // Settings -> Mints group -> Manage mints
    await page.getByRole('button', { name: 'Settings' }).click();
    await page.getByRole('button', { name: 'Manage mints' }).click();
    await expect(page).toHaveURL(/#\/settings\/mints$/);

    // empty state, and the public-mint suggestion is offered
    await expect(page.getByText('No mints yet', { exact: false })).toBeVisible();
    const suggestion = page.getByRole('button', { name: '@mint.600.wtf' });
    await expect(suggestion).toBeVisible();
    await suggestion.click();

    // the mint lands in the trusted list with its key fingerprint
    const mintsList = page.locator('.q-list', { hasText: 'Your mints' });
    await expect(mintsList.getByText('mint.600.wtf')).toBeVisible();
    await expect(mintsList.getByText(`Key ${MINT_PUBKEY.slice(0, 10)}`)).toBeVisible();
    await expect(mintsList.getByText('0 sats held here')).toBeVisible();
    // the trusted suggestion disappears from the suggestion row
    await expect(page.getByRole('button', { name: '@mint.600.wtf' })).toHaveCount(0);

    // set as default, then clear
    await mintsList.getByRole('button', { name: 'Set default' }).click();
    await expect(mintsList.locator('.q-badge', { hasText: 'Default' })).toBeVisible();
    await mintsList.getByRole('button', { name: 'Clear default' }).click();
    await expect(mintsList.locator('.q-badge')).toHaveCount(0);
    await mintsList.getByRole('button', { name: 'Set default' }).click();
    await expect(mintsList.locator('.q-badge', { hasText: 'Default' })).toBeVisible();

    // remove: confirm dialog, then back to the empty state
    await mintsList.getByRole('button', { name: 'Remove' }).click();
    const confirm = page.locator('.q-dialog', { hasText: 'Remove mint' });
    await confirm.getByRole('button', { name: 'Remove' }).click();
    await expect(mintsList.getByText('mint.600.wtf')).toHaveCount(0);
    await expect(page.getByText('No mints yet', { exact: false })).toBeVisible();
  });

  test('a mint with held notes cannot be removed', async ({ page, mint }) => {
    // hold a 21-sat note from the mock mint - holding funds locks the mint
    // against removal
    await mint.mockNoteInfo({ amountMsat: 21_000, mintPubkey: MINT_PUBKEY });
    await mint.mockRotateOk();
    await createFreshWallet(page);

    await page.getByRole('button', { name: 'Receive' }).click();
    const chooser = page.locator('.q-dialog', { hasText: 'Paste or scan a note' });
    await chooser.getByRole('button', { name: 'Bearer note' }).click();
    const dialog = page.locator('.q-dialog', { hasText: 'Receive bearer note' });
    await dialog
      .locator('textarea')
      .fill(buildNoteUrl(`${MINT_ORIGIN}${NOTE_PATH}`, defaultRandomSecret(), 21_000));
    await dialog.getByRole('button', { name: 'Receive', exact: true }).click();
    await expect(dialog.getByText('Received 21 sats')).toBeVisible();
    // the note advertises a mint key, so the first-contact trust prompt
    // opens as its own dialog - trust it (the mint stays locked either way,
    // since we hold its note)
    await page
      .locator('.q-dialog', { hasText: 'New mint' })
      .getByRole('button', { name: 'Trust this mint' })
      .click();
    await dialog.getByRole('button', { name: 'Done' }).click();

    await page.goto('/#/settings/mints');
    const mintsList = page.locator('.q-list', { hasText: 'Your mints' });
    await expect(mintsList.getByText('mint.test')).toBeVisible();
    await expect(mintsList.getByText('21 sats held here')).toBeVisible();

    await mintsList.getByRole('button', { name: 'Remove' }).click();
    const confirm = page.locator('.q-dialog', { hasText: 'Remove mint' });
    await confirm.getByRole('button', { name: 'Remove' }).click();

    // the locked-mint error surfaces as a friendly banner, entry stays
    await expect(
      page.locator('.q-banner', { hasText: "can't be removed while you hold notes" }),
    ).toBeVisible();
    await expect(mintsList.getByText('mint.test')).toBeVisible();
  });
});
