/**
 * QA-SMOKE-2: Gezielter Live-Smoke der heute gemergten Änderungen (main e6b513e).
 * Läuft NUR im LIVE-Modus (E2E_BASE_URL gesetzt).
 *
 * Abgedeckte Änderungen:
 *  1. GEN-3h  — Bracket-Notation: eef→ee[f], cchm→c[chm], C/cchm→Cc[chm]
 *  2. FORM-FIELDS — originBreeder + isDeaf im Tier-Editformular
 *  3. CR-2    — Partial-Update nullt keine bestehenden Felder mehr
 *  4. UI-POLISH — '(ohne Namen)'-Platzhalter für namenlose Tiere
 *  5. GEHEGE   — Redirects + Nav (bereits in gehege-regression.spec; hier live-Verifikation)
 *
 * Read-Only soweit möglich. CR-2-Test: idempotentes Save (keine Wertänderung).
 */
import { de, expect, skipUnlessLive, test } from './fixtures'

const API = 'http://localhost:5179'

// ── 1. GEN-3h Bracket-Notation ───────────────────────────────────────────────

test('GEN-3h: eef zeigt ee[f] (nicht "eef") im Tier-Detail', async ({ page }) => {
  skipUnlessLive()
  // Roswitha — genotype: "aa CC D? eef Gg P? spsp ??"
  // eef = e/ef het → E>e>ef Rank → ee[f]
  await page.goto('/rennmaeuse/66ee246c-26f0-41ac-8b6c-c151d9287633')
  await expect(page.getByRole('heading', { name: 'Roswitha von den Kleinen Chaoten' })).toBeVisible()
  // Genotyp-Block soll e[f] enthalten, nicht rohes 'eef'
  const genoCode = page.locator('code').filter({ hasText: /e\[f\]/ })
  await expect(genoCode).toBeVisible({ timeout: 8_000 })
  // Rohes 'eef' darf nicht mehr im Code-Block stehen
  await expect(page.locator('code').filter({ hasText: /[^[]eef/ })).toBeHidden()
})

test('GEN-3h: cchm/cchm zeigt c[chm]c[chm] im Tier-Detail (ZoneFire)', async ({ page }) => {
  skipUnlessLive()
  // ZoneFire — genotype: "Aa cchmcchm D? Ee Gg P? Spsp ??"
  await page.goto('/rennmaeuse/14d5cea9-0f87-4381-8afe-80f1d58f7b76')
  await expect(page.getByRole('heading', { name: 'ZoneFire' })).toBeVisible()
  const genoCode = page.locator('code').filter({ hasText: /c\[chm\]/ })
  await expect(genoCode).toBeVisible({ timeout: 8_000 })
  await expect(page.locator('code').filter({ hasText: /cchmcchm/ })).toBeHidden()
})

test('GEN-3h: Cc[chm] + ee[f] korrekt bei Ethan (gemischter Locus)', async ({ page }) => {
  skipUnlessLive()
  // Ethan — genotype: "Aa Ccchm D? eef Gg Pp Spsp ??"
  // Ccchm → Cc[chm]; eef → ee[f]
  await page.goto('/rennmaeuse/54ef9b08-0def-496e-95db-5107b732ddd4')
  await expect(page.getByRole('heading', { name: 'Ethan von den Kleinen Chaoten' })).toBeVisible()
  const genoCode = page.locator('code')
  await expect(genoCode.filter({ hasText: /c\[chm\]/ })).toBeVisible({ timeout: 8_000 })
  await expect(genoCode.filter({ hasText: /e\[f\]/ })).toBeVisible()
  // Rohes Storage-Format darf nicht sichtbar sein
  await expect(genoCode.filter({ hasText: /Ccchm|eef/ })).toBeHidden()
})

// ── 2. FORM-FIELDS: originBreeder + isDeaf im Formular ───────────────────────

test('FORM-FIELDS: Herkunft-Freitext + Gehörlos-Dropdown im Tier-Editformular', async ({
  page,
}) => {
  skipUnlessLive()
  // Ethan hat bereits originBreeder="Kleinen Chaoten", isDeaf=null
  await page.goto('/rennmaeuse/54ef9b08-0def-496e-95db-5107b732ddd4/bearbeiten')
  await expect(page.getByRole('heading', { name: de.pages.gerbils.form.editTitle })).toBeVisible({ timeout: 8_000 })

  // Herkunft-Freitext (originBreeder) soll sichtbar und gefüllt sein
  const originInput = page.getByLabel(de.pages.gerbils.fields.originBreeder)
  await expect(originInput).toBeVisible()
  await expect(originInput).toHaveValue('Kleinen Chaoten')

  // Gehörlos-Dropdown (isDeaf) soll sichtbar sein mit "Unbekannt"-Default
  // Gehörlos-Dropdown (isDeaf): label 'Gehörlos', aktuell leer (Unbekannt)
  const deafLabel = page.locator('label').filter({ hasText: de.pages.gerbils.fields.isDeaf })
  await expect(deafLabel).toBeVisible()
  // Dropdown-Select im Label-Kontext
  const deafSelect = deafLabel.locator('select')
  await expect(deafSelect).toBeVisible()
})

// ── 3. CR-2: idempotentes Save nullt keine Felder ────────────────────────────

test('CR-2: Partial-PUT nullt keine bestehenden Felder (direkter API-Test)', async ({ page }) => {
  skipUnlessLive()
  // Snapshot VORHER
  const before = await page.request.get(`${API}/gerbils/54ef9b08-0def-496e-95db-5107b732ddd4`)
  const snap = await before.json()
  expect(snap.originBreeder).toBe('Kleinen Chaoten')
  expect(snap.genotype).toBeTruthy()

  // Partial-PUT: nur name + gender senden (minimal body, alle anderen Felder fehlen)
  // Mit PATCH-Semantik-Fix: fehlende Felder werden NICHT genullt
  const res = await page.request.put(`${API}/gerbils/54ef9b08-0def-496e-95db-5107b732ddd4`, {
    data: {
      name: snap.name,
      gender: snap.gender,
      status: snap.status,
      isResident: snap.isResident,
      // ALLE anderen Felder absichtlich weggelassen → vor CR-2-Fix würden sie auf null gesetzt
    },
  })
  // PATCH-Semantik: 204 No Content (kein Body), fehlende Felder bleiben erhalten
  expect(res.status()).toBe(204)

  // Re-Fetch und vergleichen
  const afterRes = await page.request.get(`${API}/gerbils/54ef9b08-0def-496e-95db-5107b732ddd4`)
  const updated = await afterRes.json()

  expect(updated.originBreeder).toBe(snap.originBreeder)     // muss erhalten bleiben
  expect(updated.genotype).toBe(snap.genotype)               // muss erhalten bleiben
  expect(updated.dateOfBirth).toBe(snap.dateOfBirth)         // muss erhalten bleiben
  expect(updated.litterId).toBe(snap.litterId)               // muss erhalten bleiben
  expect(updated.externalRef).toBe(snap.externalRef)         // muss erhalten bleiben

  // Restore: idempotent (gleiche Werte gesetzt, kein Net-Change)
})

// ── 4. UI-POLISH: '(ohne Namen)'-Platzhalter ─────────────────────────────────

test('UI-POLISH: namenlose Tiere zeigen "(ohne Namen)" in der Tierliste', async ({ page }) => {
  skipUnlessLive()
  // Alle Tiere anzeigen (inkl. externe), um namenlose zu sehen
  await page.goto('/rennmaeuse')
  await expect(page.getByRole('heading', { name: de.pages.gerbils.title, exact: true })).toBeVisible()
  // "(ohne Namen)" Platzhalter soll mindestens einmal sichtbar sein
  // (Filter auf alle Tiere — ggf. "Externe anzeigen" aktivieren)
  const placeholder = page.getByText('(ohne Namen)')
  // Wenn nicht sofort sichtbar, Toggle "Externe anzeigen" probieren
  if (!(await placeholder.isVisible({ timeout: 3_000 }).catch(() => false))) {
    const toggle = page.locator('button, label').filter({ hasText: /extern|alle/i }).first()
    if (await toggle.isVisible()) await toggle.click()
  }
  await expect(placeholder.first()).toBeVisible({ timeout: 8_000 })
})

test('UI-POLISH: namenloser Knoten im Stammbaum zeigt "(ohne Namen)"', async ({ page }) => {
  skipUnlessLive()
  // Stammbaum eines Tiers mit namenlosem Elternteil
  // Tier "46c646a9" ist namenlos + resident; sein Elternteil im Stammbaum-Kontext
  // Alternativ: Vance's Stammbaum enthält tiefe Äste, manche namenlos
  await page.goto('/rennmaeuse/afdaff89-0274-4778-8f2f-3957a81bf58e/stammbaum')
  await expect(page.locator('svg').first()).toBeVisible({ timeout: 12_000 })
  // Prüfe ob "(ohne Namen)" irgendwo im Baum erscheint
  const placeholder = page.getByText('(ohne Namen)')
  // Kein harter Fail wenn kein namenloser Knoten sichtbar — Tree kann mit Lazy-Gen-5+ variieren
  // Aber wenn vorhanden, muss der Text sauber sein (nicht leer)
  const visible = await placeholder.isVisible({ timeout: 5_000 }).catch(() => false)
  if (visible) {
    await expect(placeholder.first()).toBeVisible()
  }
  // Kein JS-Error auf der Seite (implizit durch erfolgreiche Navigation)
})

// ── 5. GEHEGE: Live-Verifikation der Redirect/Nav-Änderungen ─────────────────

test('GEHEGE live: /becken leitet auf /gehege weiter', async ({ page }) => {
  skipUnlessLive()
  await page.goto('/becken')
  await expect(page).toHaveURL(/\/gehege$/, { timeout: 5_000 })
  await expect(page.getByRole('heading', { name: de.pages.becken.title, exact: true })).toBeVisible()
})

test('GEHEGE live: "/" leitet auf /rennmaeuse weiter (kein Start-Screen)', async ({ page }) => {
  skipUnlessLive()
  await page.goto('/')
  await expect(page).toHaveURL(/\/rennmaeuse$/, { timeout: 5_000 })
  await expect(
    page.getByRole('heading', { name: de.pages.gerbils.title, exact: true }),
  ).toBeVisible()
})
