import { describe, test, expect } from 'vitest';
import { classifyTimelineFailure, detectSoftBlock, isNotFoundPage, isPrivatePage } from './classify.js';
import { IgRateLimitError, IgLoginRequiredError, IgAccountNotFoundError, IgPrivateAccountError } from './errors.js';

const base = { timelineMatched: 0, sawHomeRedirect: false, sawLoginRedirect: false, loginWall: false, notFoundPage: false, privatePage: false, graphqlSeen: 3, username: 'x' };

describe('classifyTimelineFailure', () => {
  test('null when the timeline matched (success)', () => {
    expect(classifyTimelineFailure({ ...base, timelineMatched: 2 })).toBeNull();
  });
  test('login wall or login-redirect -> IgLoginRequiredError', () => {
    expect(classifyTimelineFailure({ ...base, loginWall: true })).toBeInstanceOf(IgLoginRequiredError);
    expect(classifyTimelineFailure({ ...base, sawLoginRedirect: true })).toBeInstanceOf(IgLoginRequiredError);
  });
  test('home-redirect (302 to /) with no match -> IgRateLimitError', () => {
    expect(classifyTimelineFailure({ ...base, sawHomeRedirect: true })).toBeInstanceOf(IgRateLimitError);
  });
  test('404 shell (deleted/renamed account) -> IgAccountNotFoundError', () => {
    expect(classifyTimelineFailure({ ...base, notFoundPage: true })).toBeInstanceOf(IgAccountNotFoundError);
  });
  test('throttle signal beats the 404 shell (never mark dead during a block)', () => {
    expect(classifyTimelineFailure({ ...base, notFoundPage: true, sawHomeRedirect: true })).toBeInstanceOf(IgRateLimitError);
  });
  test('private shell -> IgPrivateAccountError', () => {
    expect(classifyTimelineFailure({ ...base, privatePage: true })).toBeInstanceOf(IgPrivateAccountError);
  });
  test('throttle beats the private shell too (never mark private during a block)', () => {
    expect(classifyTimelineFailure({ ...base, privatePage: true, sawHomeRedirect: true })).toBeInstanceOf(IgRateLimitError);
  });
  test('nothing matched and no signal -> generic Error', () => {
    const e = classifyTimelineFailure(base)!;
    expect(e).toBeInstanceOf(Error);
    expect(e).not.toBeInstanceOf(IgRateLimitError);
    expect(e).not.toBeInstanceOf(IgLoginRequiredError);
    expect(e).not.toBeInstanceOf(IgAccountNotFoundError);
    expect(e).not.toBeInstanceOf(IgPrivateAccountError);
  });
});

describe('isPrivatePage', () => {
  test('Korean/English private shells hit; a bio mentioning 비공개 does not', () => {
    expect(isPrivatePage('이 계정은 비공개 계정입니다')).toBe(true);
    expect(isPrivatePage('This account is private')).toBe(true);
    expect(isPrivatePage('This Account is Private Follow to see their photos')).toBe(true);
    expect(isPrivatePage('비공개 후기를 공유하는 계정 · 게시물 320')).toBe(false);
    expect(isPrivatePage('Posts Followers Following')).toBe(false);
  });
});

describe('isNotFoundPage', () => {
  test("Korean/English 404 shells hit; throttle shell doesn't", () => {
    expect(isNotFoundPage('죄송합니다. 페이지를 사용할 수 없습니다. 클릭하신 링크가 잘못되었거나 페이지가 삭제되었습니다.')).toBe(true);
    expect(isNotFoundPage("Sorry, this page isn't available.")).toBe(true);
    expect(isNotFoundPage('Sorry, this page isn’t available.')).toBe(true);
    expect(isNotFoundPage('문제가 발생했습니다 문제가 발생하여 페이지를 읽어들이지 못했습니다.')).toBe(false);
    expect(isNotFoundPage('Posts Followers Following Reels')).toBe(false);
  });
});

describe('detectSoftBlock', () => {
  test('reCAPTCHA iframe present -> hit', () => {
    const r = detectSoftBlock({ bodyText: '', recaptchaCount: 1 });
    expect(r.hit).toBe(true);
    expect(r.reason).toContain('recaptcha');
  });
  test('Korean bot-check text -> hit', () => {
    expect(detectSoftBlock({ bodyText: '로봇이 아닙니다', recaptchaCount: 0 }).hit).toBe(true);
  });
  test('Korean throttle text -> hit', () => {
    expect(detectSoftBlock({ bodyText: '잠시 후 다시 시도해 주세요', recaptchaCount: 0 }).hit).toBe(true);
  });
  test('English throttle text -> hit', () => {
    expect(detectSoftBlock({ bodyText: 'Please wait a few minutes before you try again.', recaptchaCount: 0 }).hit).toBe(true);
  });
  test('clean profile body -> no hit, reason none', () => {
    const r = detectSoftBlock({ bodyText: 'Posts Followers Following Reels', recaptchaCount: 0 });
    expect(r.hit).toBe(false);
    expect(r.reason).toBe('none');
  });
});
