import { describe, test, expect } from 'bun:test';
import { analyzeTocPage } from '../src/services/analyzer.ts';

const SCOPE = '#list-chapter .chapter-archive-grid';
const PATTERN = 'https://novelbin\\.com/b/the-mech-touch/.*';

function makeHtml(opts: {
  title?: string;
  body?: string;
  links?: string[];
  extraHead?: string;
}) {
  const links = (opts.links ?? [])
    .map(href => `<a href="${href}">link</a>`)
    .join('');
  return `<html>
    <head>
      <title>${opts.title ?? 'Test Page'}</title>
      ${opts.extraHead ?? ''}
    </head>
    <body>${opts.body ?? ''}${links}</body>
  </html>`;
}

describe('analyzer — Cloudflare detection', () => {
  test('flags "Just a moment" title as Cloudflare', () => {
    const html = makeHtml({ title: 'Just a moment...' });
    const d = analyzeTocPage(html, SCOPE, PATTERN);
    const cf = d.findings.find(f => f.check === 'Cloudflare challenge')!;
    expect(cf.passed).toBe(false);
    expect(d.likelyCause).toContain('Cloudflare');
  });

  test('flags #challenge-form element as Cloudflare', () => {
    const html = makeHtml({ body: '<form id="challenge-form"></form>' });
    const d = analyzeTocPage(html, SCOPE, PATTERN);
    const cf = d.findings.find(f => f.check === 'Cloudflare challenge')!;
    expect(cf.passed).toBe(false);
  });

  test('does not flag a clean page as Cloudflare', () => {
    const html = makeHtml({ title: 'The Mech Touch — Chapters', body: '<div id="list-chapter"><div class="chapter-archive-grid"></div></div>' });
    const d = analyzeTocPage(html, SCOPE, PATTERN);
    const cf = d.findings.find(f => f.check === 'Cloudflare challenge')!;
    expect(cf.passed).toBe(true);
  });
});

describe('analyzer — security block detection', () => {
  test('flags "you have been blocked" body text', () => {
    const html = makeHtml({ body: '<p>You have been blocked from accessing this site.</p>' });
    const d = analyzeTocPage(html, SCOPE, PATTERN);
    const check = d.findings.find(f => f.check === 'Security / bot block')!;
    expect(check.passed).toBe(false);
    expect(d.likelyCause).toContain('Security block');
  });
});

describe('analyzer — CAPTCHA detection', () => {
  test('flags g-recaptcha div', () => {
    const html = makeHtml({ body: '<div class="g-recaptcha" data-sitekey="abc"></div>' });
    const d = analyzeTocPage(html, SCOPE, PATTERN);
    const check = d.findings.find(f => f.check === 'CAPTCHA')!;
    expect(check.passed).toBe(false);
  });
});

describe('analyzer — login wall detection', () => {
  test('flags password input', () => {
    const html = makeHtml({ body: '<form><input type="password"/></form>' });
    const d = analyzeTocPage(html, SCOPE, PATTERN);
    const check = d.findings.find(f => f.check === 'Login wall')!;
    expect(check.passed).toBe(false);
    expect(d.likelyCause).toContain('Login');
  });
});

describe('analyzer — scope selector breakdown', () => {
  test('reports root missing when neither segment exists', () => {
    const html = makeHtml({ body: '<div id="something-else"></div>' });
    const d = analyzeTocPage(html, SCOPE, PATTERN);
    const check = d.findings.find(f => f.check === 'TOC scope selector')!;
    expect(check.passed).toBe(false);
    expect(check.detail).toContain('#list-chapter');
    expect(check.detail).toContain('not found anywhere');
    expect(d.likelyCause).toContain('redesigned');
  });

  test('reports inner selector missing when root exists but child does not', () => {
    const html = makeHtml({ body: '<div id="list-chapter"><ul><li>no grid here</li></ul></div>' });
    const d = analyzeTocPage(html, SCOPE, PATTERN);
    const check = d.findings.find(f => f.check === 'TOC scope selector')!;
    expect(check.passed).toBe(false);
    expect(check.detail).toContain('#list-chapter');
    expect(check.detail).toContain('.chapter-archive-grid');
    expect(d.likelyCause).toContain('Inner selector');
  });

  test('notes when inner selector exists elsewhere on the page (wrong hierarchy)', () => {
    const html = makeHtml({
      body: '<div id="list-chapter"></div><div class="chapter-archive-grid"></div>',
    });
    const d = analyzeTocPage(html, SCOPE, PATTERN);
    const check = d.findings.find(f => f.check === 'TOC scope selector')!;
    expect(check.passed).toBe(false);
    expect(check.detail).toContain('exists elsewhere');
  });

  test('passes when full scope matches', () => {
    const html = makeHtml({
      body: '<div id="list-chapter"><div class="chapter-archive-grid"></div></div>',
    });
    const d = analyzeTocPage(html, SCOPE, PATTERN);
    const check = d.findings.find(f => f.check === 'TOC scope selector')!;
    expect(check.passed).toBe(true);
  });
});

describe('analyzer — link pattern check', () => {
  test('finds matching links outside the scope when scope is missing', () => {
    const links = [
      'https://novelbin.com/b/the-mech-touch/chapter-1',
      'https://novelbin.com/b/the-mech-touch/chapter-2',
    ];
    const html = makeHtml({ body: '<div id="other">' + links.map(l => `<a href="${l}">ch</a>`).join('') + '</div>' });
    const d = analyzeTocPage(html, SCOPE, PATTERN);
    const check = d.findings.find(f => f.check === 'Link pattern (anywhere)')!;
    expect(check.passed).toBe(true);
    expect(check.detail).toContain('2 link');
    expect(d.likelyCause).toContain('scope selector');
  });

  test('reports no matching links and samples what is there', () => {
    const html = makeHtml({ links: ['https://other.com/a', 'https://other.com/b'] });
    const d = analyzeTocPage(html, SCOPE, PATTERN);
    const check = d.findings.find(f => f.check === 'Link pattern (anywhere)')!;
    expect(check.passed).toBe(false);
    expect(check.detail).toContain('other.com');
  });
});

describe('analyzer — actualUrl', () => {
  test('includes the landed URL in the diagnosis', () => {
    const html = makeHtml({});
    const d = analyzeTocPage(html, SCOPE, PATTERN, 'https://novelbin.com/redirected');
    expect(d.actualUrl).toBe('https://novelbin.com/redirected');
  });
});
