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

const adminAuth = {
  authenticated: true,
  csrfToken: 'test-csrf',
  user: {
    id: 1,
    email: 'admin@learnitive.test',
    displayName: 'Template Admin',
    avatarUrl: null,
    role: 'admin',
    status: 'active',
  },
  entitlement: {
    tier: 'pro',
    pro: true,
    blocked: false,
    quota: 1000,
    usage: 0,
    remaining: 1000,
  },
} as const;

function json(route: Route, status: number, body: unknown) {
  return route.fulfill({
    status,
    contentType: 'application/json',
    body: JSON.stringify(body),
  });
}

function adminTemplate(thumbnail = '') {
  return {
    id: 'sample-paper',
    slug: 'sample-paper',
    name: 'Sample Paper',
    tier: 'free',
    status: 'published',
    category: 'Conference papers',
    provenance: 'learnitive',
    entryFile: 'main.tex',
    version: '1.0.0',
    thumbnail,
    preview: '',
    previewResolved: '',
    createdAt: '2026-08-24T00:00:00.000Z',
    modifiedAt: '2026-08-24T00:00:00.000Z',
  };
}

async function mockAdminThumbnailApi(page: Page) {
  let thumbnail = '';
  let uploadCount = 0;
  let deleteCount = 0;

  await page.route('**/api/**', async (route) => {
    const request = route.request();
    const url = new URL(request.url());
    const method = request.method();

    if (url.pathname === '/api/config/public') {
      return json(route, 200, {
        saasEnabled: true,
        googleAuthEnabled: false,
        contactEnabled: false,
        shopUrl: '',
      });
    }
    if (url.pathname === '/api/auth/me') return json(route, 200, adminAuth);
    if (url.pathname === '/api/user/settings' && method === 'GET') {
      return json(route, 200, { settings: {} });
    }
    if (url.pathname === '/api/admin/templates' && method === 'GET') {
      return json(route, 200, {
        items: [adminTemplate(thumbnail)],
        institutions: [],
        categories: [],
      });
    }
    if (url.pathname === '/api/admin/templates/sample-paper/thumbnail' && method === 'POST') {
      uploadCount += 1;
      thumbnail = 'thumbnail.webp';
      return json(route, 200, {
        item: adminTemplate(thumbnail),
        mime: 'image/webp',
        bytes: 21_076,
        sync: { total: 1 },
      });
    }
    if (url.pathname === '/api/admin/templates/sample-paper/thumbnail' && method === 'DELETE') {
      deleteCount += 1;
      thumbnail = '';
      return json(route, 200, {
        item: adminTemplate(thumbnail),
        sync: { total: 1 },
      });
    }
    if (url.pathname === '/api/templates/sample-paper/assets/thumbnail') {
      return route.fulfill({
        status: 200,
        contentType: 'image/webp',
        body: Buffer.from('UklGRgAAAABXRUJQVlA4TAQAAAAvAAAAAA==', 'base64'),
      });
    }

    return json(route, 404, { error: 'not_mocked' });
  });

  return {
    uploadCount: () => uploadCount,
    deleteCount: () => deleteCount,
  };
}

test('Admin can upload, replace and remove the catalog thumbnail without editing a path', async ({ page }) => {
  const api = await mockAdminThumbnailApi(page);
  await page.goto('/admin/templates');
  await page.getByRole('button', { name: 'Edit Sample Paper' }).click();

  const editor = page.locator('.interactive-admin-template-editor');
  await expect(editor.getByText('Default catalog thumbnail:')).toBeVisible();
  await expect(editor.getByAltText('Default Learnitive template thumbnail')).toHaveAttribute(
    'src',
    '/learnitive-template-default-thumbnail.webp',
  );

  const thumbnailInput = editor.locator('.interactive-admin-template-thumbnail-upload input[type="file"]');
  await thumbnailInput.setInputFiles({
    name: 'sample-thumbnail.webp',
    mimeType: 'image/webp',
    buffer: Buffer.from('UklGRgAAAABXRUJQVlA4TAQAAAAvAAAAAA==', 'base64'),
  });

  await expect(editor.getByRole('button', { name: 'Replace thumbnail' })).toBeVisible();
  await expect(editor.getByRole('button', { name: 'Remove thumbnail' })).toBeVisible();
  await expect(editor.getByText('Custom thumbnail:')).toBeVisible();
  await expect(editor.getByText('thumbnail.webp')).toBeVisible();
  expect(api.uploadCount()).toBe(1);

  await editor.getByRole('button', { name: 'Remove thumbnail' }).click();
  await expect(editor.getByRole('button', { name: 'Upload thumbnail' })).toBeVisible();
  await expect(editor.getByText('Default catalog thumbnail:')).toBeVisible();
  await expect(editor.getByAltText('Default Learnitive template thumbnail')).toHaveAttribute(
    'src',
    '/learnitive-template-default-thumbnail.webp',
  );
  expect(api.deleteCount()).toBe(1);
});

function manifestTemplate(slug: string, name: string, thumbnail?: string) {
  return {
    id: slug,
    slug,
    name,
    description: `${name} description`,
    category: 'Conference papers',
    author: 'Learnitive',
    tier: 'free',
    status: 'published',
    degreeLevels: [],
    requiredPackages: [],
    features: [],
    tags: [],
    entryFile: 'main.tex',
    templateVersion: '1.0.0',
    thumbnail,
    files: [{
      path: 'main.tex',
      url: `/api/templates/${slug}/files/main.tex`,
      size: 12,
    }],
  };
}

test('template catalog uses explicit thumbnails and the shared A4 fallback, not preview assets', async ({ page }) => {
  await page.route('**/learnitive-templates/templates.json', (route) => json(route, 200, {
    version: 1,
    generatedAt: '2026-08-24T00:00:00.000Z',
    contentHash: 'thumbnail-e2e',
    templates: [
      {
        ...manifestTemplate('custom-thumb', 'Custom Thumbnail', '/api/templates/custom-thumb/assets/thumbnail'),
        preview: '/api/templates/custom-thumb/assets/preview',
        previewKind: 'pdf',
      },
      {
        ...manifestTemplate('fallback-thumb', 'Fallback Thumbnail'),
        preview: '/api/templates/fallback-thumb/assets/preview',
        previewKind: 'pdf',
      },
    ],
  }));
  await page.route('**/api/templates/categories', (route) => json(route, 200, { categories: [] }));
  await page.route('**/api/templates/institutions', (route) => json(route, 200, { institutions: [] }));

  await page.goto('/templates');

  const custom = page.locator('.interactive-template-card').filter({ hasText: 'Custom Thumbnail' }).first();
  const fallback = page.locator('.interactive-template-card').filter({ hasText: 'Fallback Thumbnail' }).first();
  await expect(custom.locator('.interactive-template-card-preview-link > img')).toHaveAttribute(
    'src',
    '/api/templates/custom-thumb/assets/thumbnail',
  );
  await expect(fallback.locator('.interactive-template-card-preview-link > img')).toHaveAttribute(
    'src',
    '/learnitive-template-default-thumbnail.webp',
  );

  const aspectRatio = await fallback.locator('.interactive-template-card-preview-link > img').evaluate(
    (element) => getComputedStyle(element).aspectRatio,
  );
  expect(aspectRatio).toBe('210 / 297');
});


test('catalog cards use thumbnails while Quick Preview and details prefer an uploaded PDF preview', async ({ page }) => {
  const template = {
    ...manifestTemplate('pdf-and-thumbnail', 'PDF And Thumbnail', '/api/templates/pdf-and-thumbnail/assets/thumbnail'),
    preview: '/api/templates/pdf-and-thumbnail/assets/preview',
    previewKind: 'pdf' as const,
  };
  await page.route('**/learnitive-templates/templates.json', (route) => json(route, 200, {
    version: 1,
    generatedAt: '2026-08-25T00:00:00.000Z',
    contentHash: 'pdf-thumbnail-split-e2e',
    templates: [template],
  }));
  await page.route('**/api/templates/categories', (route) => json(route, 200, { categories: [] }));
  await page.route('**/api/templates/institutions', (route) => json(route, 200, { institutions: [] }));

  await page.goto('/templates');
  const card = page.locator('.interactive-template-card').filter({ hasText: 'PDF And Thumbnail' }).first();
  await expect(card.locator('.interactive-template-card-preview-link > img')).toHaveAttribute(
    'src',
    '/api/templates/pdf-and-thumbnail/assets/thumbnail',
  );

  await card.click();
  await expect(page.getByRole('dialog').locator('.interactive-template-pdfjs')).toBeVisible();
  await page.getByRole('link', { name: 'View Details' }).click();
  await expect(page.locator('.interactive-template-detail .interactive-template-pdfjs')).toBeVisible();
});

test('template cards open Quick Preview as one clickable surface and Fresh starting points follows All templates', async ({ page }) => {
  const templates = Array.from({ length: 5 }, (_, index) => manifestTemplate(`card-${index + 1}`, `Card ${index + 1}`));
  await page.route('**/learnitive-templates/templates.json', (route) => json(route, 200, {
    version: 1,
    generatedAt: '2026-08-24T00:00:00.000Z',
    contentHash: 'card-e2e',
    templates,
  }));
  await page.route('**/api/templates/categories', (route) => json(route, 200, { categories: [] }));
  await page.route('**/api/templates/institutions', (route) => json(route, 200, { institutions: [] }));

  await page.goto('/templates');
  const firstCard = page.locator('.interactive-template-card').filter({ hasText: 'Card 1' }).first();
  await expect(firstCard.getByText('Quick Preview')).toHaveCount(0);
  await expect(firstCard.getByText('View details', { exact: false })).toHaveCount(0);
  await firstCard.click();
  await expect(page.getByRole('dialog')).toContainText('Card 1');
  await page.keyboard.press('Escape');

  const allTemplates = page.getByRole('heading', { name: 'All templates' });
  const fresh = page.getByRole('heading', { name: 'Fresh starting points' });
  const order = await allTemplates.evaluate((all, freshNode) => {
    if (!(freshNode instanceof Node)) return false;
    return Boolean(all.compareDocumentPosition(freshNode) & Node.DOCUMENT_POSITION_FOLLOWING);
  }, await fresh.elementHandle());
  expect(order).toBe(true);
  await expect(page.locator('.interactive-template-featured-grid .interactive-template-card')).toHaveCount(4);
});
