/**
 * Demo.docx Feature Tests
 *
 * Comprehensive tests for the demo.docx calibre document which exercises:
 * - Title with bottom border (horizontal rule)
 * - Inline formatting (bold, italic, underline, strikethrough, superscript, subscript)
 * - Text colors (red, green, blue, theme colors)
 * - Highlighting (yellow)
 * - Paragraph formatting (right align, background, borders)
 * - Tables (simple, styled with colors, nested, calendar)
 * - Footnotes and endnotes references
 * - Dropcaps
 * - Hyperlinks (external and internal)
 * - Table of Contents
 * - Images (inline, floating)
 * - Lists (bullet, numbered, multi-level, roman numerals, continued)
 */

import { test, expect } from '@playwright/test';
import { EditorPage } from '../helpers/editor-page';
import * as path from 'path';

const DEMO_DOCX_PATH = 'fixtures/demo/demo.docx';

test.describe('Demo.docx - Document Loading', () => {
  let editor: EditorPage;

  test.beforeEach(async ({ page }) => {
    editor = new EditorPage(page);
    await editor.goto();
    await editor.waitForReady();
  });

  test('loads demo.docx successfully', async ({ page }) => {
    await editor.loadDocxFile(DEMO_DOCX_PATH);

    // Verify document loaded by checking for title text
    await expect(page.locator('.ProseMirror')).toContainText(
      'Demonstration of DOCX support in calibre'
    );
  });

  test('shows correct filename after loading', async ({ page }) => {
    await editor.loadDocxFile(DEMO_DOCX_PATH);

    // Filename should appear in header
    await expect(page.locator('text=demo.docx')).toBeVisible();
  });
});

test.describe('Demo.docx - Title Formatting', () => {
  let editor: EditorPage;

  test.beforeEach(async ({ page }) => {
    editor = new EditorPage(page);
    await editor.goto();
    await editor.waitForReady();
    await editor.loadDocxFile(DEMO_DOCX_PATH);
  });

  test('title has large font size', async ({ page }) => {
    const title = page
      .locator('.ProseMirror')
      .locator('text=Demonstration of DOCX support')
      .first();

    // Title should be visible and styled
    await expect(title).toBeVisible();

    // Check that the title paragraph has large font (26pt = 52 half-points)
    const fontSize = await page.evaluate(() => {
      const el = document.evaluate(
        "//*[contains(text(), 'Demonstration of DOCX support')]",
        document,
        null,
        XPathResult.FIRST_ORDERED_NODE_TYPE,
        null
      ).singleNodeValue as HTMLElement;
      if (el) {
        return window.getComputedStyle(el).fontSize;
      }
      return null;
    });

    // Font size should be at least 24px (larger than body text)
    expect(parseFloat(fontSize || '0')).toBeGreaterThanOrEqual(24);
  });

  test('title has theme color', async ({ page }) => {
    const titleColor = await page.evaluate(() => {
      const el = document.evaluate(
        "//*[contains(text(), 'Demonstration of DOCX support')]",
        document,
        null,
        XPathResult.FIRST_ORDERED_NODE_TYPE,
        null
      ).singleNodeValue as HTMLElement;
      if (el) {
        return window.getComputedStyle(el).color;
      }
      return null;
    });

    // Title should have a dark blue color (theme text2 with shade)
    // The exact color depends on theme resolution, but it should be blue-ish
    expect(titleColor).toBeTruthy();
  });

  test('title has bottom border (horizontal rule)', async ({ page }) => {
    const titleParagraph = page.locator('.ProseMirror p').first();

    const borderBottom = await titleParagraph.evaluate((el) => {
      return window.getComputedStyle(el).borderBottom;
    });

    // Should have a visible bottom border (theme accent1 blue)
    expect(borderBottom).toContain('solid');
    expect(borderBottom).toMatch(/rgb\(\d+,\s*\d+,\s*\d+\)/); // Should have a color
  });
});

test.describe('Demo.docx - Inline Text Formatting', () => {
  let editor: EditorPage;

  test.beforeEach(async ({ page }) => {
    editor = new EditorPage(page);
    await editor.goto();
    await editor.waitForReady();
    await editor.loadDocxFile(DEMO_DOCX_PATH);
  });

  test('renders bold text', async ({ page }) => {
    // Find the bold text "bold"
    const boldElement = page.locator('strong:has-text("bold")').first();
    await expect(boldElement).toBeVisible();
  });

  test('renders italic text', async ({ page }) => {
    // Find italic text
    const italicElement = page.locator('em:has-text("italic")').first();
    await expect(italicElement).toBeVisible();
  });

  test('renders bold-italic text', async ({ page }) => {
    // Find bold-italic text
    const boldItalicElement = page.locator('strong em:has-text("bold-italic")');
    await expect(boldItalicElement).toBeVisible();
  });

  test('renders underlined text', async ({ page }) => {
    const hasUnderline = await page.evaluate(() => {
      const walker = document.createTreeWalker(
        document.querySelector('.ProseMirror') || document.body,
        NodeFilter.SHOW_TEXT,
        null
      );
      while (walker.nextNode()) {
        if (walker.currentNode.textContent?.includes('underlined')) {
          let el = walker.currentNode.parentElement;
          while (el) {
            const style = window.getComputedStyle(el);
            if (style.textDecorationLine.includes('underline')) {
              return true;
            }
            el = el.parentElement;
          }
        }
      }
      return false;
    });

    expect(hasUnderline).toBe(true);
  });

  test('renders strikethrough text', async ({ page }) => {
    const hasStrike = await page.evaluate(() => {
      const walker = document.createTreeWalker(
        document.querySelector('.ProseMirror') || document.body,
        NodeFilter.SHOW_TEXT,
        null
      );
      while (walker.nextNode()) {
        if (walker.currentNode.textContent?.includes('struck out')) {
          let el = walker.currentNode.parentElement;
          while (el) {
            const style = window.getComputedStyle(el);
            if (style.textDecorationLine.includes('line-through')) {
              return true;
            }
            el = el.parentElement;
          }
        }
      }
      return false;
    });

    expect(hasStrike).toBe(true);
  });

  test('renders superscript text', async ({ page }) => {
    const superscript = page.locator('sup:has-text("script")').first();
    await expect(superscript).toBeVisible();
  });

  test('renders subscript text', async ({ page }) => {
    const subscript = page.locator('sub:has-text("script")').first();
    await expect(subscript).toBeVisible();
  });

  test('renders red colored text', async ({ page }) => {
    const hasRedText = await page.evaluate(() => {
      const walker = document.createTreeWalker(
        document.querySelector('.ProseMirror') || document.body,
        NodeFilter.SHOW_TEXT,
        null
      );
      while (walker.nextNode()) {
        if (walker.currentNode.textContent?.trim() === 'red') {
          let el = walker.currentNode.parentElement;
          while (el) {
            const style = window.getComputedStyle(el);
            const color = style.color;
            // Red should have high R value, low G and B
            const match = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
            if (match) {
              const [, r, g, b] = match.map(Number);
              if (r > 200 && g < 100 && b < 100) {
                return true;
              }
            }
            el = el.parentElement;
          }
        }
      }
      return false;
    });

    expect(hasRedText).toBe(true);
  });

  test('renders yellow highlighted text', async ({ page }) => {
    const highlightedText = page.locator('mark:has-text("yellow highlight")');
    await expect(highlightedText).toBeVisible();
  });
});

test.describe('Demo.docx - Tables', () => {
  let editor: EditorPage;

  test.beforeEach(async ({ page }) => {
    editor = new EditorPage(page);
    await editor.goto();
    await editor.waitForReady();
    await editor.loadDocxFile(DEMO_DOCX_PATH);
  });

  test('renders tables', async ({ page }) => {
    // Demo document has multiple tables
    const tableCount = await page.locator('.ProseMirror table').count();
    expect(tableCount).toBeGreaterThan(0);
  });

  test('renders simple ITEM/NEEDED table', async ({ page }) => {
    // Find the table with "ITEM" header
    const itemCell = page.locator('table th:has-text("ITEM"), table td:has-text("ITEM")').first();
    await expect(itemCell).toBeVisible();

    const neededCell = page
      .locator('table th:has-text("NEEDED"), table td:has-text("NEEDED")')
      .first();
    await expect(neededCell).toBeVisible();
  });

  test('renders table with Books, Pens, Pencils rows', async ({ page }) => {
    await expect(page.locator('table td:has-text("Books")').first()).toBeVisible();
    await expect(page.locator('table td:has-text("Pens")').first()).toBeVisible();
    await expect(page.locator('table td:has-text("Pencils")').first()).toBeVisible();
  });

  test('renders distance table with Point A-E', async ({ page }) => {
    await expect(page.locator('table:has-text("Point A")').first()).toBeVisible();
    await expect(page.locator('table:has-text("Point B")').first()).toBeVisible();
  });

  test('renders college enrollment table', async ({ page }) => {
    await expect(page.locator('table:has-text("Cedar University")').first()).toBeVisible();
  });

  test('renders nested table', async ({ page }) => {
    // The document has a table with "One", "Two", "Three", "Four" in nested structure
    await expect(page.locator('table:has-text("One")').first()).toBeVisible();
  });

  test('renders calendar table', async ({ page }) => {
    await expect(page.locator('table:has-text("December 2007")').first()).toBeVisible();
  });

  test('renders table header cells with background color from style', async ({ page }) => {
    // The ITEM/NEEDED table has styled header row with green background
    const itemHeader = page.locator('table th:has-text("ITEM")').first();
    await expect(itemHeader).toBeVisible();

    // Verify the header has green background color from table style
    const bgColor = await itemHeader.evaluate((el) => getComputedStyle(el).backgroundColor);
    // Table style applies rgb(155, 187, 89) - a green color
    expect(bgColor).toBe('rgb(155, 187, 89)');
  });

  test('renders College table header with blue background from style', async ({ page }) => {
    // The College enrollment table has blue header background
    const collegeHeader = page.locator('table th:has-text("College")').first();
    await expect(collegeHeader).toBeVisible();

    // Verify the header has blue background color from table style
    const bgColor = await collegeHeader.evaluate((el) => getComputedStyle(el).backgroundColor);
    // Table style applies rgb(75, 172, 198) - a blue color
    expect(bgColor).toBe('rgb(75, 172, 198)');
  });
});

test.describe('Demo.docx - Hyperlinks', () => {
  let editor: EditorPage;

  test.beforeEach(async ({ page }) => {
    editor = new EditorPage(page);
    await editor.goto();
    await editor.waitForReady();
    await editor.loadDocxFile(DEMO_DOCX_PATH);
  });

  test('renders external hyperlink to calibre', async ({ page }) => {
    const link = page.locator('a:has-text("calibre download page")').first();
    await expect(link).toBeVisible();

    const href = await link.getAttribute('href');
    expect(href).toContain('calibre');
  });

  test('renders internal document link', async ({ page }) => {
    const link = page.locator('a:has-text("paragraph level formatting")').first();
    await expect(link).toBeVisible();

    const href = await link.getAttribute('href');
    // Internal links use # anchor
    expect(href).toContain('#');
  });
});

test.describe('Demo.docx - Lists', () => {
  let editor: EditorPage;

  test.beforeEach(async ({ page }) => {
    editor = new EditorPage(page);
    await editor.goto();
    await editor.waitForReady();
    await editor.loadDocxFile(DEMO_DOCX_PATH);
  });

  test('renders bulleted list items', async ({ page }) => {
    // Look for list items with bullet markers
    const bulletItems = page.locator('.ProseMirror').locator('text=One').first();
    await expect(bulletItems).toBeVisible();
  });

  test('document contains "Bulleted List" heading', async ({ page }) => {
    await expect(page.locator('.ProseMirror')).toContainText('Bulleted List');
  });

  test('document contains "Numbered List" heading', async ({ page }) => {
    await expect(page.locator('.ProseMirror')).toContainText('Numbered List');
  });

  test('document contains "Multi-level Lists" heading', async ({ page }) => {
    await expect(page.locator('.ProseMirror')).toContainText('Multi-level Lists');
  });

  test('document contains "Continued Lists" heading', async ({ page }) => {
    await expect(page.locator('.ProseMirror')).toContainText('Continued Lists');
  });
});

test.describe('Demo.docx - Images', () => {
  let editor: EditorPage;

  test.beforeEach(async ({ page }) => {
    editor = new EditorPage(page);
    await editor.goto();
    await editor.waitForReady();
    await editor.loadDocxFile(DEMO_DOCX_PATH);
  });

  test('renders images', async ({ page }) => {
    // The demo document contains several images
    const imageCount = await page.locator('.ProseMirror img').count();
    expect(imageCount).toBeGreaterThan(0);
  });

  test('images have src attribute', async ({ page }) => {
    // Wait for images to load
    await page.waitForTimeout(500);

    const images = page.locator('.ProseMirror img');
    const count = await images.count();

    // If there are images, check they have src
    if (count > 0) {
      for (let i = 0; i < Math.min(count, 3); i++) {
        const img = images.nth(i);
        const src = await img.getAttribute('src');
        // Images should have data URLs or blob URLs
        if (src) {
          expect(src.length).toBeGreaterThan(0);
          return; // At least one image has src
        }
      }
    }

    // If we get here, no images had src - check if any images exist at all
    expect(count).toBeGreaterThan(0);
  });
});

test.describe('Demo.docx - Paragraph Formatting', () => {
  let editor: EditorPage;

  test.beforeEach(async ({ page }) => {
    editor = new EditorPage(page);
    await editor.goto();
    await editor.waitForReady();
    await editor.loadDocxFile(DEMO_DOCX_PATH);
  });

  test('renders right-aligned paragraph with background', async ({ page }) => {
    // The "Paragraph level formatting" section has a right-aligned gray paragraph
    const rightAlignedPara = await page.evaluate(() => {
      const paras = document.querySelectorAll('.ProseMirror p');
      for (const p of paras) {
        const style = window.getComputedStyle(p);
        if (style.textAlign === 'right' && p.textContent?.includes('crazy things')) {
          return {
            textAlign: style.textAlign,
            backgroundColor: style.backgroundColor,
          };
        }
      }
      return null;
    });

    expect(rightAlignedPara?.textAlign).toBe('right');
  });

  test('renders heading styles with proper colors', async ({ page }) => {
    // Headings like "Text Formatting", "Tables" etc. should have blue color
    const textFormattingHeading = page
      .locator('.ProseMirror')
      .locator('text=Text Formatting')
      .first();
    await expect(textFormattingHeading).toBeVisible();
  });
});

test.describe('Demo.docx - Structural Elements', () => {
  let editor: EditorPage;

  test.beforeEach(async ({ page }) => {
    editor = new EditorPage(page);
    await editor.goto();
    await editor.waitForReady();
    await editor.loadDocxFile(DEMO_DOCX_PATH);
  });

  test('document contains Footnotes section', async ({ page }) => {
    await expect(page.locator('.ProseMirror')).toContainText('Footnotes & Endnotes');
  });

  test('footnote references are rendered as superscript', async ({ page }) => {
    // Check in visible pages that footnote refs have superscript styling
    const supRun = page
      .locator('.paged-editor__pages span[style*="vertical-align: super"]')
      .first();
    await expect(supRun).toBeVisible();

    const display = await supRun.evaluate((el) => {
      return window.getComputedStyle(el).verticalAlign;
    });
    expect(display).toContain('super');
  });

  test('endnote references are rendered as superscript', async ({ page }) => {
    // Endnote references should be rendered with the docx-endnote-ref class
    const endnoteRef = page.locator('.ProseMirror .docx-endnote-ref').first();
    await expect(endnoteRef).toBeVisible();
  });

  test('document contains Dropcaps section', async ({ page }) => {
    await expect(page.locator('.ProseMirror')).toContainText('Dropcaps');
  });

  test('document contains Table of Contents section', async ({ page }) => {
    await expect(page.locator('.ProseMirror')).toContainText('Table of Contents');
  });

  test('dropcap D is rendered', async ({ page }) => {
    // The dropcap "D" should be present (currently rendered as plain text)
    const largeD = await page.evaluate(() => {
      const walker = document.createTreeWalker(
        document.querySelector('.ProseMirror') || document.body,
        NodeFilter.SHOW_TEXT,
        null
      );
      while (walker.nextNode()) {
        // Look for standalone "D" that is the dropcap
        if (walker.currentNode.textContent?.trim() === 'D') {
          return true;
        }
      }
      return false;
    });

    expect(largeD).toBe(true);
  });
});

test.describe('Demo.docx - Table of Contents', () => {
  let editor: EditorPage;

  test.beforeEach(async ({ page }) => {
    editor = new EditorPage(page);
    await editor.goto();
    await editor.waitForReady();
    await editor.loadDocxFile(DEMO_DOCX_PATH);
  });

  test('TOC entries are hyperlinks', async ({ page }) => {
    // TOC entries should be clickable links
    const tocLink = page.locator('a:has-text("Text Formatting")').first();
    await expect(tocLink).toBeVisible();
  });

  test('TOC contains main sections', async ({ page }) => {
    await expect(page.locator('.ProseMirror')).toContainText('Tables');
    await expect(page.locator('.ProseMirror')).toContainText('Structural Elements');
    await expect(page.locator('.ProseMirror')).toContainText('Images');
    await expect(page.locator('.ProseMirror')).toContainText('Lists');
  });
});

test.describe('Demo.docx - Round-trip Save', () => {
  let editor: EditorPage;

  test.beforeEach(async ({ page }) => {
    editor = new EditorPage(page);
    await editor.goto();
    await editor.waitForReady();
    await editor.loadDocxFile(DEMO_DOCX_PATH);
  });

  test('can edit and save document', async ({ page }) => {
    // Add some text at the end
    await editor.focus();

    // Press Ctrl+End to go to end of document
    const modifier = process.platform === 'darwin' ? 'Meta' : 'Control';
    await page.keyboard.press(`${modifier}+End`);

    // Type some text
    await editor.typeText('\n\nEdited by test');

    // Verify text was added
    await expect(page.locator('.ProseMirror')).toContainText('Edited by test');
  });

  test('saved document preserves formatting', async ({ page }) => {
    // Verify basic text editing works (save/re-load infrastructure not yet available)
    await editor.typeText('Edited by test');
    await expect(page.locator('.ProseMirror')).toContainText('Edited by test');
  });
});

test.describe('Demo.docx - Font Support', () => {
  let editor: EditorPage;

  test.beforeEach(async ({ page }) => {
    editor = new EditorPage(page);
    await editor.goto();
    await editor.waitForReady();
    await editor.loadDocxFile(DEMO_DOCX_PATH);
  });

  test('renders Ubuntu Mono font for monospace text', async ({ page }) => {
    // The document mentions "Ubuntu Mono typeface" - look for monospace text
    const hasMonospace = await page.evaluate(() => {
      const el = document.evaluate(
        "//*[contains(text(), 'Ubuntu Mono typeface')]",
        document,
        null,
        XPathResult.FIRST_ORDERED_NODE_TYPE,
        null
      ).singleNodeValue as HTMLElement;
      return el !== null;
    });

    expect(hasMonospace).toBe(true);
  });
});
