// Logs + deathSave characterization. Lock paths for log writes, undo, clear, death save.

import React from 'react';
import { screen, fireEvent, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import { getCalls } from '../__mocks__/firebase/_mock-db';
import { setupReady, addMonsterViaUI, startCombatViaUI } from './testHelpers';

function findLogCalls() {
  return getCalls().filter(c => c.fn === 'addDoc' && c.path.includes('/logs'));
}
function lastEncCall() {
  const calls = getCalls().filter(c => c.fn === 'updateDoc' && c.path.includes('/encounters/'));
  return calls[calls.length - 1];
}

// Navigate to /logs view. App reads pathname at mount; must re-render with path preset.
import { render } from '@testing-library/react';
import App from '../App';
async function goToLogs() {
  // unmount current tree isn't needed; App checks pathname in useEffect.
  // Re-render a fresh App instance in same container.
  window.history.replaceState({}, '', '/logs');
  document.body.innerHTML = '';
  render(<App />);
  await waitFor(() => screen.getByText(/Combat Log/i));
}

describe('Logs -> Firebase', () => {
  test('logAction: addDoc to logs collection on combat start', async () => {
    await setupReady('LogCamp', 'LogEnc');
    await addMonsterViaUI('Mob', 10, 2);
    await startCombatViaUI();
    await waitFor(() => findLogCalls().some(c => /Combat started/.test(c.data.message)));
    const logCall = findLogCalls().find(c => /Combat started/.test(c.data.message));
    expect(logCall.data).toHaveProperty('message');
    expect(logCall.data).toHaveProperty('timestamp');
    expect(logCall.data.message).toMatch(/Combat started/);
  });

  test('logAction: includes undo payload', async () => {
    await setupReady('UndoCamp', 'UndoEnc');
    await addMonsterViaUI('Mob', 10, 2);
    await startCombatViaUI();
    await waitFor(() => findLogCalls().some(c => /Combat started/.test(c.data.message)));
    const logCall = findLogCalls().find(c => /Combat started/.test(c.data.message));
    expect(logCall.data.undo).toBeTruthy();
    expect(logCall.data.undo).toHaveProperty('updates');
  });

  test('clearLogs: writeBatch deletes all log docs', async () => {
    const { renderApp } = require('./testHelpers');
    // seed a log entry via combat start
    await setupReady('ClearCamp', 'ClearEnc');
    await addMonsterViaUI('Mob', 10, 2);
    await startCombatViaUI();
    await waitFor(() => findLogCalls().length > 0);

    await goToLogs();
    const clearBtn = await screen.findByRole('button', { name: /Clear Log/i });
    fireEvent.click(clearBtn);
    fireEvent.click(await screen.findByRole('button', { name: /Confirm/i }));

    await waitFor(() => {
      const batchDeletes = getCalls().filter(c => c.fn === 'batch.delete' && c.path.includes('/logs'));
      return batchDeletes.length > 0;
    });
    const batchDeletes = getCalls().filter(c => c.fn === 'batch.delete' && c.path.includes('/logs'));
    expect(batchDeletes.length).toBeGreaterThan(0);
  });

  test('undo: updateDoc on encounter path + marks log undone', async () => {
    // seed log via combat start
    await setupReady('UndoFlowCamp', 'UndoFlowEnc');
    await addMonsterViaUI('Mob', 10, 2);
    await startCombatViaUI();
    await waitFor(() => findLogCalls().length > 0);
    const logId = findLogCalls()[0].path.split('/').pop();

    await goToLogs();
    const undoBtns = await screen.findAllByRole('button', { name: /Undo/i });
    fireEvent.click(undoBtns[0]);

    await waitFor(() => {
      const und = getCalls().find(c => c.fn === 'updateDoc' && c.path.endsWith(`/logs/${logId}`) && c.data.undone === true);
      return und;
    });
    const markUndone = getCalls().find(c => c.fn === 'updateDoc' && c.path.endsWith(`/logs/${logId}`));
    expect(markUndone.data.undone).toBe(true);
    // encounter path updated with undo payload (any encounter update after undo click)
    const encUndo = getCalls().filter(c => c.fn === 'updateDoc' && c.path.includes('/encounters/'));
    expect(encUndo.length).toBeGreaterThan(0);
  });
});

describe('DeathSave -> Firebase', () => {
  test('first death save: updateDoc increments deathSaves', async () => {
    const { renderApp, createCampaignViaUI, selectCampaignByName, createEncounterViaUI, selectEncounterByName, getParticipantForm, startCombatViaUI } = require('./testHelpers');
    const { within } = require('@testing-library/react');
    await renderApp();
    await createCampaignViaUI('DSC2');
    await selectCampaignByName('DSC2');
    fireEvent.change(screen.getByPlaceholderText('Character name'), { target: { value: 'Hero' } });
    fireEvent.change(screen.getByLabelText(/Default HP/i), { target: { value: '10' } });
    fireEvent.click(screen.getByRole('button', { name: /Add Character/i }));
    await waitFor(() => {
      const c = getCalls().find(c => c.fn === 'updateDoc' && c.path.includes('/campaigns/') && c.data.players?.length === 1);
      return c;
    });
    const charId = getCalls().find(c => c.fn === 'updateDoc' && c.path.includes('/campaigns/') && c.data.players?.length === 1).data.players[0].id;

    await createEncounterViaUI('DSEnc2');
    await selectEncounterByName('DSEnc2');
    // switch to character type and add
    const form = within(getParticipantForm());
    fireEvent.change(form.getByDisplayValue('Monster'), { target: { value: 'character' } });
    fireEvent.change(form.getByDisplayValue('-- Select from Campaign --'), { target: { value: charId } });
    fireEvent.click(form.getByRole('button', { name: /Add to Encounter/i }));
    await waitFor(() => lastEncCall()?.data?.participants?.[0]?.name === 'Hero');

    await startCombatViaUI();
    // damage to 0
    fireEvent.change(screen.getByPlaceholderText('HP'), { target: { value: '10' } });
    fireEvent.click(screen.getByTitle('Damage'));
    await waitFor(() => lastEncCall()?.data?.participants?.[0]?.currentHp === 0);

    // death save buttons appear
    const save1 = screen.getByTitle('Death save 1');
    fireEvent.click(save1);
    await waitFor(() => lastEncCall()?.data?.participants?.[0]?.deathSaves === 1);
    expect(lastEncCall().data.participants[0].deathSaves).toBe(1);
  });

  test('third death save: marks isDying true', async () => {
    const { renderApp, createCampaignViaUI, selectCampaignByName, createEncounterViaUI, selectEncounterByName, getParticipantForm } = require('./testHelpers');
    const { within } = require('@testing-library/react');
    await renderApp();
    await createCampaignViaUI('DSDie');
    await selectCampaignByName('DSDie');
    fireEvent.change(screen.getByPlaceholderText('Character name'), { target: { value: 'Martyr' } });
    fireEvent.change(screen.getByLabelText(/Default HP/i), { target: { value: '10' } });
    fireEvent.click(screen.getByRole('button', { name: /Add Character/i }));
    await waitFor(() => {
      const c = getCalls().find(c => c.fn === 'updateDoc' && c.path.includes('/campaigns/') && c.data.players?.length === 1);
      return c;
    });
    const charId = getCalls().find(c => c.fn === 'updateDoc' && c.path.includes('/campaigns/') && c.data.players?.length === 1).data.players[0].id;

    await createEncounterViaUI('DSEncDie');
    await selectEncounterByName('DSEncDie');
    const form = within(getParticipantForm());
    fireEvent.change(form.getByDisplayValue('Monster'), { target: { value: 'character' } });
    fireEvent.change(form.getByDisplayValue('-- Select from Campaign --'), { target: { value: charId } });
    fireEvent.click(form.getByRole('button', { name: /Add to Encounter/i }));
    await waitFor(() => lastEncCall()?.data?.participants?.[0]?.name === 'Martyr');

    await startCombatViaUI();
    fireEvent.change(screen.getByPlaceholderText('HP'), { target: { value: '10' } });
    fireEvent.click(screen.getByTitle('Damage'));
    await waitFor(() => lastEncCall()?.data?.participants?.[0]?.currentHp === 0);

    fireEvent.click(screen.getByTitle('Death save 1'));
    await waitFor(() => lastEncCall()?.data?.participants?.[0]?.deathSaves === 1);
    fireEvent.click(screen.getByTitle('Death save 2'));
    await waitFor(() => lastEncCall()?.data?.participants?.[0]?.deathSaves === 2);
    fireEvent.click(screen.getByTitle('Death save 3'));
    await waitFor(() => lastEncCall()?.data?.participants?.[0]?.isDying === true);
    expect(lastEncCall().data.participants[0].isDying).toBe(true);
    expect(lastEncCall().data.participants[0].deathSaves).toBe(3);
  });
});
