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

/**
 * E2E tests for Product CRUD operations.
 *
 * Prerequisites:
 * - Frontend running on http://localhost:3001 (npm run dev)
 * - Backend running on http://localhost:8081 (docker compose up)
 * - Auth setup must run first (profile selected)
 *
 * These tests create a temporary product category, use it to test
 * the full product CRUD, then clean up both.
 */

const UNIQUE = Date.now()
const TEST_CATEGORY_NAME = `E2E Cat Produit ${UNIQUE}`
const PRODUCT_NAME = `E2E Produit Test ${UNIQUE}`
const PRODUCT_REFERENCE = `REF-E2E-${UNIQUE}`
const PRODUCT_PRICE = '42.50'
const PRODUCT_NAME_UPDATED = `${PRODUCT_NAME} modifié`
const PRODUCT_REFERENCE_UPDATED = `${PRODUCT_REFERENCE}-UPD`
const PRODUCT_PRICE_UPDATED = '99.99'

// ──────────────────────────────────────────────
// Helpers
// ──────────────────────────────────────────────

/**
 * Creates a product category via the UI.
 */
async function createTestCategory(page: Page) {
  await page.goto('/product-category/new')
  await page.locator('#model-type-name').fill(TEST_CATEGORY_NAME)
  await page.locator('button[type="submit"]').click()
  await expect(page).toHaveURL('/product-category', { timeout: 10_000 })
  await expect(page.getByText('Catégorie de produit créée avec succès')).toBeVisible()
}

/**
 * Selects an option in a SearchSelect component.
 *
 * The SearchSelect renders:
 *   .search-select > .relative > input[placeholder]
 *   .search-select > .relative > div (dropdown) > ul > li > button
 */
async function selectSearchOption(page: Page, placeholder: string, searchText: string) {
  const input = page.getByPlaceholder(placeholder)
  await input.click()
  await input.fill(searchText)

  // The dropdown is inside .search-select > .relative > div > ul > li > button
  const option = page.locator('.search-select ul li button')
    .filter({ hasText: searchText })
    .first()
  await option.waitFor({ state: 'visible', timeout: 10_000 })
  await option.click()
}

/**
 * Cleans up test data: deletes the category via the UI.
 */
async function cleanupTestCategory(page: Page) {
  await page.goto('/product-category')
  // Wait for list to load
  await page.waitForTimeout(1_000)

  const row = page.getByRole('row').filter({ hasText: TEST_CATEGORY_NAME })
  if (await row.isVisible().catch(() => false)) {
    await row.getByRole('button', { name: 'Supprimer' }).click()
    const confirmBtn = page.locator('button.btn-error').filter({ hasText: 'Supprimer' })
    await confirmBtn.waitFor({ state: 'visible', timeout: 5_000 })
    await confirmBtn.click()
    await page.waitForTimeout(1_000)
  }
}

test.describe('Product CRUD', () => {
  test.describe.configure({ mode: 'serial' })

  // ──────────────────────────────────────────────
  // SETUP: Create a test category
  // ──────────────────────────────────────────────

  test('setup: create a test product category', async ({ page }) => {
    await createTestCategory(page)
  })

  // ──────────────────────────────────────────────
  // LIST PAGE
  // ──────────────────────────────────────────────

  test('should display the product catalog page', async ({ page }) => {
    await page.goto('/product-catalog')
    await expect(page.getByRole('heading', { name: 'Catalogue des produits' })).toBeVisible({ timeout: 10_000 })
    await expect(page.getByRole('link', { name: /ajouter un produit/i })).toBeVisible()
    await expect(page.getByRole('link', { name: /gérer les catégories/i })).toBeVisible()
  })

  test('should navigate to create product page', async ({ page }) => {
    await page.goto('/product-catalog')
    await page.getByRole('link', { name: /ajouter un produit/i }).click()
    await expect(page).toHaveURL('/product/create')
    await expect(page.getByRole('heading', { name: 'Nouveau produit' })).toBeVisible()
  })

  // ──────────────────────────────────────────────
  // CREATE
  // ──────────────────────────────────────────────

  test('should show disabled fields until category is selected', async ({ page }) => {
    await page.goto('/product/create')

    // Name input should be disabled before selecting a category
    const nameInput = page.getByPlaceholder('Nom affiché dans le catalogue')
    await expect(nameInput).toBeDisabled()
  })

  test('should create a product with all fields', async ({ page }) => {
    await page.goto('/product/create')

    // 1. Select category via SearchSelect
    await selectSearchOption(page, 'Rechercher une catégorie...', TEST_CATEGORY_NAME)

    // Wait for form to enable after category selection
    const nameInput = page.getByPlaceholder('Nom affiché dans le catalogue')
    await expect(nameInput).toBeEnabled({ timeout: 5_000 })

    // 2. Fill form fields
    await nameInput.clear()
    await nameInput.fill(PRODUCT_NAME)

    const referenceInput = page.getByPlaceholder('Référence interne ou fournisseur')
    await referenceInput.fill(PRODUCT_REFERENCE)

    const priceInput = page.getByPlaceholder('Valeur indicatrice')
    await priceInput.fill(PRODUCT_PRICE)

    // 3. Submit
    await page.getByRole('button', { name: /créer le produit/i }).click()

    // Should redirect to catalog and show success
    await expect(page).toHaveURL('/product-catalog', { timeout: 15_000 })
    await expect(page.getByText('Produit créé avec succès')).toBeVisible()
  })

  // ──────────────────────────────────────────────
  // READ
  // ──────────────────────────────────────────────

  test('should display the created product in the catalog', async ({ page }) => {
    await page.goto('/product-catalog')
    await expect(page.getByText(PRODUCT_NAME)).toBeVisible({ timeout: 10_000 })
  })

  test('should show product reference in the catalog', async ({ page }) => {
    await page.goto('/product-catalog')
    await expect(page.getByText(PRODUCT_REFERENCE)).toBeVisible({ timeout: 10_000 })
  })

  test('should find the product via search', async ({ page }) => {
    await page.goto('/product-catalog')

    const searchInput = page.getByPlaceholder('Nom ou référence…')
    await searchInput.fill(PRODUCT_REFERENCE)
    // Wait for client-side filtering
    await page.waitForTimeout(300)

    await expect(page.getByText(PRODUCT_NAME)).toBeVisible()
    await expect(page.getByText(PRODUCT_REFERENCE)).toBeVisible()
  })

  test('should show category link in the catalog', async ({ page }) => {
    await page.goto('/product-catalog')
    await expect(page.getByText(PRODUCT_NAME)).toBeVisible({ timeout: 10_000 })

    const row = page.getByRole('row').filter({ hasText: PRODUCT_NAME })
    await expect(row.getByText(TEST_CATEGORY_NAME)).toBeVisible()
  })

  test('should sort products by name', async ({ page }) => {
    await page.goto('/product-catalog')
    await page.locator('#product-sort').selectOption('name')
    await page.locator('#product-dir').selectOption('asc')
    await expect(page.getByRole('heading', { name: 'Catalogue des produits' })).toBeVisible()
  })

  test('should sort products by creation date', async ({ page }) => {
    await page.goto('/product-catalog')
    await page.locator('#product-sort').selectOption('createdAt')
    await page.locator('#product-dir').selectOption('desc')
    await expect(page.getByRole('heading', { name: 'Catalogue des produits' })).toBeVisible()
  })

  // ──────────────────────────────────────────────
  // UPDATE
  // ──────────────────────────────────────────────

  test('should navigate to edit page from catalog', async ({ page }) => {
    await page.goto('/product-catalog')
    await expect(page.getByText(PRODUCT_NAME)).toBeVisible({ timeout: 10_000 })

    const row = page.getByRole('row').filter({ hasText: PRODUCT_NAME })
    await row.getByRole('link', { name: 'Modifier' }).click()

    await expect(page.getByRole('heading', { name: 'Modifier le produit' })).toBeVisible({ timeout: 10_000 })
  })

  test('should show category note on edit page', async ({ page }) => {
    await page.goto('/product-catalog')
    await expect(page.getByText(PRODUCT_NAME)).toBeVisible({ timeout: 10_000 })

    const row = page.getByRole('row').filter({ hasText: PRODUCT_NAME })
    await row.getByRole('link', { name: 'Modifier' }).click()
    await expect(page.getByRole('heading', { name: 'Modifier le produit' })).toBeVisible({ timeout: 10_000 })

    // Category should be displayed but disabled
    await expect(page.getByText("La catégorie d'origine ne peut pas être modifiée")).toBeVisible()
  })

  test('should update the product', async ({ page }) => {
    await page.goto('/product-catalog')
    await expect(page.getByText(PRODUCT_NAME)).toBeVisible({ timeout: 10_000 })

    const row = page.getByRole('row').filter({ hasText: PRODUCT_NAME })
    await row.getByRole('link', { name: 'Modifier' }).click()
    await expect(page.getByRole('heading', { name: 'Modifier le produit' })).toBeVisible({ timeout: 10_000 })

    // Update name (label-text "Nom du produit" → sibling input)
    const nameInput = page.locator('.form-control').filter({ hasText: 'Nom du produit' }).locator('input')
    await nameInput.clear()
    await nameInput.fill(PRODUCT_NAME_UPDATED)

    // Update reference
    const refInput = page.locator('.form-control').filter({ hasText: 'Référence' }).locator('input')
    await refInput.clear()
    await refInput.fill(PRODUCT_REFERENCE_UPDATED)

    // Update price
    const priceInput = page.locator('.form-control').filter({ hasText: 'Prix fournisseur' }).locator('input')
    await priceInput.clear()
    await priceInput.fill(PRODUCT_PRICE_UPDATED)

    // Submit
    await page.getByRole('button', { name: /enregistrer les modifications/i }).click()

    await expect(page).toHaveURL('/product-catalog', { timeout: 10_000 })
    await expect(page.getByText('Produit mis à jour avec succès')).toBeVisible()
  })

  test('should display updated product in catalog', async ({ page }) => {
    await page.goto('/product-catalog')
    await expect(page.getByText(PRODUCT_NAME_UPDATED)).toBeVisible({ timeout: 10_000 })
    await expect(page.getByText(PRODUCT_REFERENCE_UPDATED)).toBeVisible()
  })

  // ──────────────────────────────────────────────
  // DELETE
  // ──────────────────────────────────────────────

  test('should cancel product deletion', async ({ page }) => {
    await page.goto('/product-catalog')
    await expect(page.getByText(PRODUCT_NAME_UPDATED)).toBeVisible({ timeout: 10_000 })

    const row = page.getByRole('row').filter({ hasText: PRODUCT_NAME_UPDATED })
    await row.getByRole('button', { name: 'Supprimer' }).click()

    // Confirmation modal
    await expect(page.getByText(/voulez-vous vraiment supprimer/i)).toBeVisible()
    await page.getByRole('button', { name: 'Annuler' }).click()

    // Product should still be here
    await expect(page.getByText(PRODUCT_NAME_UPDATED)).toBeVisible()
  })

  test('should delete the product', async ({ page }) => {
    await page.goto('/product-catalog')
    await expect(page.getByText(PRODUCT_NAME_UPDATED)).toBeVisible({ timeout: 10_000 })

    const row = page.getByRole('row').filter({ hasText: PRODUCT_NAME_UPDATED })
    await row.getByRole('button', { name: 'Supprimer' }).click()

    // Confirm deletion in modal
    await expect(page.getByText(/voulez-vous vraiment supprimer/i)).toBeVisible()
    await page.locator('button.btn-error').filter({ hasText: 'Supprimer' }).click()

    await expect(page.getByText(/supprimé/i)).toBeVisible({ timeout: 10_000 })
    // The toast message contains the product name, so check the table specifically
    const table = page.locator('table')
    await expect(table.getByText(PRODUCT_NAME_UPDATED)).not.toBeVisible({ timeout: 5_000 })
  })

  // ──────────────────────────────────────────────
  // CLEANUP: Remove the test category
  // ──────────────────────────────────────────────

  test('cleanup: delete the test product category', async ({ page }) => {
    await cleanupTestCategory(page)
  })
})
