import { test, expect } from '@playwright/test';
import path from 'node:path';
import fs from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import { getStagingConfig, requireStagingConfig } from './helpers/staging-config';
import { assertStagingHealth, pollUntil } from './helpers/staging-health';
import {
  fetchUnreadCount,
  loginWithMagicLink,
  loginWithOtp,
  logoutFromHeader,
} from './helpers/staging-auth';
import {
  buildTicketSubject,
  cleanupStagingTickets,
  fetchMailDeliveryReport,
} from './helpers/staging-cleanup';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const config = getStagingConfig();
const artifactsDir = path.resolve(__dirname, '../../../../../', config.artifactsDir);

const state = {
  ticketId: '',
  ticketSubject: '',
  ticketPath: '',
  initialUnreadCount: 0,
};

async function saveArtifact(name: string, page: import('@playwright/test').Page): Promise<void> {
  await fs.mkdir(artifactsDir, { recursive: true });
  await page.screenshot({ path: path.join(artifactsDir, `${name}.png`), fullPage: true });
}

async function writeReport(extra: Record<string, unknown>): Promise<void> {
  await fs.mkdir(artifactsDir, { recursive: true });
  await fs.writeFile(
    path.join(artifactsDir, 'staging-run-report.json'),
    JSON.stringify(
      {
        generated_at: new Date().toISOString(),
        ticket_subject: state.ticketSubject,
        ticket_id: state.ticketId,
        ...extra,
      },
      null,
      2,
    ),
  );
}

test.describe.serial('Staging verification (live backend)', () => {
  test.skip(!config.enabled, 'Set PLAYWRIGHT_STAGING=1 to run live staging E2E');

  test.beforeAll(() => {
    requireStagingConfig(config);
  });

  test.use({
    trace: 'retain-on-failure',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
    actionTimeout: 15_000,
  });

  test.setTimeout(180_000);

  test('00 health pre-check blocks run when staging is unhealthy', async ({ request }) => {
    const health = await assertStagingHealth(request, config);
    await writeReport({ health });
    expect(health.status).not.toBe('error');
  });

  test('01 OTP login', async ({ page, request }) => {
    await loginWithOtp(page, request, config, config.requesterEmail);
    await saveArtifact('01-otp-login', page);
  });

  test('02 magic link login', async ({ page, request }) => {
    await logoutFromHeader(page);
    await loginWithMagicLink(page, request, config, config.headEmail);
    await saveArtifact('02-magic-link-login', page);
    await logoutFromHeader(page);
  });

  test('03 department portal open', async ({ page }) => {
    await page.goto(`/d/${config.portalSlug}`);
    await expect(page.getByRole('link', { name: /create ticket/i })).toBeVisible({ timeout: 20_000 });
    await saveArtifact('03-department-portal', page);
  });

  test('04 create ticket from department portal', async ({ page, request }) => {
    state.ticketSubject = buildTicketSubject(config.ticketPrefix, 'portal-ticket');

    await loginWithOtp(page, request, config, config.requesterEmail);
    await page.goto(`/d/${config.portalSlug}/create-ticket`);
    await page.getByTestId('portal-ticket-subject').fill(state.ticketSubject);
    await page.getByTestId('portal-ticket-description').fill(
      'Staging E2E ticket created from the department portal with sufficient description length.',
    );
    await page.getByTestId('portal-ticket-submit').click();

    await expect(page).toHaveURL(new RegExp(`/d/${config.portalSlug}/my-tickets`), {
      timeout: 30_000,
    });

    const ticketLink = page.getByRole('link', { name: state.ticketSubject });
    await expect(ticketLink).toBeVisible({ timeout: 30_000 });
    const href = await ticketLink.getAttribute('href');
    expect(href).toBeTruthy();
    state.ticketPath = href ?? '';
    state.ticketId = state.ticketPath.split('/').filter(Boolean).pop() ?? '';

    await saveArtifact('04-create-ticket', page);
  });

  test('05 upload attachment on reply', async ({ page, request }) => {
    await loginWithOtp(page, request, config, config.requesterEmail);
    await page.goto(state.ticketPath || `/tickets/${state.ticketId}`);

    const fileInput = page.locator('input[type="file"]').first();
    const fixturePath = path.join(__dirname, 'fixtures/staging-upload.txt');
    await fs.writeFile(
      fixturePath,
      `Staging attachment ${Date.now()}\n`,
    );
    await fileInput.setInputFiles(fixturePath);

    await page.getByTestId('ticket-composer-reply').fill('Reply with attachment from staging E2E.');
    await page.getByTestId('ticket-composer-submit').click();

    await expect(page.getByText(/reply with attachment from staging e2e/i)).toBeVisible({
      timeout: 30_000,
    });
    await saveArtifact('05-upload-attachment', page);
  });

  test('06 department head receives ticket', async ({ page, request }) => {
    await loginWithOtp(page, request, config, config.headEmail);
    await page.goto('/tickets');
    await page.getByPlaceholder(/ticket number|subject|description/i).fill(state.ticketSubject);
    await page.getByRole('button', { name: /apply filters/i }).click();

    await expect(page.getByRole('link', { name: state.ticketSubject })).toBeVisible({
      timeout: 30_000,
    });
    await saveArtifact('06-head-sees-ticket', page);
  });

  test('07 assign ticket', async ({ page, request }) => {
    await loginWithOtp(page, request, config, config.headEmail);
    await page.goto(state.ticketPath || `/tickets/${state.ticketId}`);

    await page.getByTestId('ticket-assignee-input').fill(config.headEmail.split('@')[0] ?? '');
    await page.getByRole('button', { name: new RegExp(config.headEmail, 'i') }).first().click();
    await page.getByTestId('ticket-assign-button').click();

    await expect(page.getByText(config.headEmail)).toBeVisible({ timeout: 20_000 });
    await saveArtifact('07-assign-ticket', page);
  });

  test('08 add collaborator', async ({ page, request }) => {
    await loginWithOtp(page, request, config, config.headEmail);
    await page.goto(state.ticketPath || `/tickets/${state.ticketId}`);

    const addInput = page.getByTestId('ticket-collaborator-input');
    await addInput.fill(config.collaboratorEmail.split('@')[0] ?? '');
    await page.getByRole('button', { name: new RegExp(config.collaboratorEmail, 'i') }).first().click();

    await expect(page.getByText(config.collaboratorEmail)).toBeVisible({ timeout: 20_000 });
    await saveArtifact('08-add-collaborator', page);
  });

  test('09 collaborator replies', async ({ page, request }) => {
    await loginWithOtp(page, request, config, config.collaboratorEmail);
    await page.goto(state.ticketPath || `/tickets/${state.ticketId}`);

    await page.getByTestId('ticket-composer-reply').fill('Collaborator staging reply for realtime validation.');
    await page.getByTestId('ticket-composer-submit').click();

    await expect(page.getByText(/collaborator staging reply/i)).toBeVisible({ timeout: 30_000 });
    await saveArtifact('09-collaborator-reply', page);
  });

  test('10 internal note', async ({ page, request }) => {
    await loginWithOtp(page, request, config, config.headEmail);
    await page.goto(state.ticketPath || `/tickets/${state.ticketId}`);

    await page.getByRole('tab', { name: /internal note/i }).click();
    await page.getByTestId('ticket-composer-note').fill('Staging internal note — not visible to requester.');
    await page.getByTestId('ticket-composer-submit').click();

    await expect(page.getByText(/staging internal note/i)).toBeVisible({ timeout: 30_000 });
    await saveArtifact('10-internal-note', page);
  });

  test('11 status update', async ({ page, request }) => {
    await loginWithOtp(page, request, config, config.headEmail);
    await page.goto(state.ticketPath || `/tickets/${state.ticketId}`);

    const statusSelect = page.getByTestId('ticket-status-select');
    const options = statusSelect.locator('option');
    const count = await options.count();
    expect(count).toBeGreaterThan(1);

    const current = await statusSelect.inputValue();
    for (let i = 0; i < count; i += 1) {
      const value = await options.nth(i).getAttribute('value');
      if (value && value !== current) {
        await statusSelect.selectOption(value);
        break;
      }
    }

    await page.getByTestId('ticket-status-update').click();
    await saveArtifact('11-status-update', page);
  });

  test('12 close ticket', async ({ page, request }) => {
    await loginWithOtp(page, request, config, config.headEmail);
    await page.goto(state.ticketPath || `/tickets/${state.ticketId}`);

    const statusSelect = page.getByTestId('ticket-status-select');
    const options = statusSelect.locator('option');
    const count = await options.count();

    for (let i = 0; i < count; i += 1) {
      const label = (await options.nth(i).textContent())?.toLowerCase() ?? '';
      if (label.includes('closed') || label.includes('resolved')) {
        const value = await options.nth(i).getAttribute('value');
        if (value) {
          await statusSelect.selectOption(value);
          break;
        }
      }
    }

    await page.getByTestId('ticket-status-update').click();
    await saveArtifact('12-close-ticket', page);
  });

  test('13 notification received', async ({ page, request }) => {
    await loginWithOtp(page, request, config, config.collaboratorEmail);

    const unread = await pollUntil(
      async () => {
        const count = await fetchUnreadCount(page);
        return count > 0 ? count : null;
      },
      { label: 'notification unread count', timeoutMs: 45_000 },
    );

    expect(unread).toBeGreaterThan(0);
    await page.getByTestId('notification-bell').click();
    await expect(page.getByRole('dialog', { name: /recent notifications/i })).toBeVisible();

    const mailReport = await fetchMailDeliveryReport(request, config, [
      config.requesterEmail,
      config.headEmail,
      config.collaboratorEmail,
    ]);

    await writeReport({ unread_notifications: unread, mail_report: mailReport });
    await saveArtifact('13-notification-received', page);
  });

  test('14 logout', async ({ page, request }) => {
    await loginWithOtp(page, request, config, config.requesterEmail);
    await logoutFromHeader(page);
    await expect(page).toHaveURL(/\/login/);
    await saveArtifact('14-logout', page);
  });

  test.afterAll(async ({ request }) => {
    if (config.enabled && config.secret) {
      await cleanupStagingTickets(request, config, state.ticketId ? [state.ticketId] : []);
    }
  });
});
