import { renderHook, act } from '@testing-library/react';
import useMediaCardWidth from '../useMediaCardWidth';
import { IMedia } from '../../types';
import {RefObject} from 'react';

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

const makePost = (overrides: Partial<IMedia> = {}): IMedia => ({
  id: 'post-1',
  type: 'image',
  caption: '',
  source: 'instagram',
  post: 'https://www.instagram.com/p/abc',
  image: 'https://cdn.example.com/image.jpg',
  productsLinked: {},
  postIndex: 0,
  ...overrides,
});

// ---------------------------------------------------------------------------
// Image mock
//
// jsdom's Image implementation never loads URLs, so we intercept the
// constructor, capture the last-created instance, and let each test control
// naturalWidth / naturalHeight and fire onload / onerror manually.
// ---------------------------------------------------------------------------

let capturedImage: {
  onload: (() => void) | null;
  onerror: (() => void) | null;
  src: string;
  naturalWidth: number;
  naturalHeight: number;
};

beforeEach(() => {
  capturedImage = { onload: null, onerror: null, src: '', naturalWidth: 0, naturalHeight: 0 };
  (global as any).Image = jest.fn().mockImplementation(() => capturedImage);
});

afterEach(() => {
  jest.restoreAllMocks();
});

// ---------------------------------------------------------------------------
// Helper: attach mock layout dimensions to the containerRef returned by the hook.
// The ref starts as null until consumer code would attach it to a DOM node;
// we replicate that here, then re-render to trigger the effect.
// ---------------------------------------------------------------------------

const attachContainer = (
  containerRef: RefObject<HTMLDivElement>,
  clientWidth: number,
  clientHeight: number,
) => {
  Object.defineProperty(containerRef, 'current', {
    value: { clientWidth, clientHeight },
    writable: true,
    configurable: true,
  });
};

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

describe('useMediaCardWidth', () => {
  it('returns "auto" before the containerRef is attached', () => {
    const { result } = renderHook(() => useMediaCardWidth(makePost()));
    // containerRef.current is still null → effect bails out
    expect(result.current.cardWidth).toBe('auto');
  });

  it('returns "auto" when the post has no image or thumbnail URL', () => {
    const post = makePost({ image: undefined, thumbnail: undefined });
    const { result } = renderHook(() => useMediaCardWidth(post));
    attachContainer(result.current.containerRef, 1000, 600);
    expect(result.current.cardWidth).toBe('auto');
  });

  it('returns "auto" when the image fails to load', () => {
    const post = makePost();
    const { result, rerender } = renderHook(({ p }) => useMediaCardWidth(p), {
      initialProps: { p: post },
    });
    attachContainer(result.current.containerRef, 1000, 600);

    // Trigger effect re-run, then fire onerror
    rerender({ p: { ...post, id: 'post-error' } });
    act(() => { capturedImage.onerror!(); });

    expect(result.current.cardWidth).toBe('auto');
  });

  it('returns "auto" when the container has zero dimensions', () => {
    const post = makePost();
    const { result, rerender } = renderHook(({ p }) => useMediaCardWidth(p), {
      initialProps: { p: post },
    });
    attachContainer(result.current.containerRef, 0, 0);

    rerender({ p: { ...post, id: 'post-zero' } });
    act(() => {
      capturedImage.naturalWidth = 800;
      capturedImage.naturalHeight = 600;
      capturedImage.onload!();
    });

    expect(result.current.cardWidth).toBe('auto');
  });

  it('uses the thumbnail URL when no image URL is present', () => {
    const post = makePost({ image: undefined, thumbnail: 'https://cdn.example.com/thumb.jpg' });
    const { result, rerender } = renderHook(({ p }) => useMediaCardWidth(p), {
      initialProps: { p: post },
    });
    attachContainer(result.current.containerRef, 1000, 600);

    rerender({ p: { ...post, id: 'post-thumb' } });
    act(() => {
      // Square thumbnail → width = height = 600 → no cap
      capturedImage.naturalWidth = 600;
      capturedImage.naturalHeight = 600;
      capturedImage.onload!();
    });

    expect(result.current.cardWidth).toBe('600px');
  });

  it('computes the natural card width for a portrait image (no cap applied)', () => {
    // 400 × 600 → ratio 0.667 → naturalCardWidth = 600 × 0.667 = 400
    // maxCardWidth = 1000 × 0.8 = 800  → min(400, 800) = 400
    const post = makePost();
    const { result, rerender } = renderHook(({ p }) => useMediaCardWidth(p), {
      initialProps: { p: post },
    });
    attachContainer(result.current.containerRef, 1000, 600);

    rerender({ p: { ...post, id: 'post-portrait' } });
    act(() => {
      capturedImage.naturalWidth = 400;
      capturedImage.naturalHeight = 600;
      capturedImage.onload!();
    });

    expect(result.current.cardWidth).toBe('400px');
  });

  it('computes the natural card width for a square image (no cap applied)', () => {
    // 600 × 600 → ratio 1 → naturalCardWidth = 600 × 1 = 600
    // maxCardWidth = 1000 × 0.8 = 800  → min(600, 800) = 600
    const post = makePost();
    const { result, rerender } = renderHook(({ p }) => useMediaCardWidth(p), {
      initialProps: { p: post },
    });
    attachContainer(result.current.containerRef, 1000, 600);

    rerender({ p: { ...post, id: 'post-square' } });
    act(() => {
      capturedImage.naturalWidth = 600;
      capturedImage.naturalHeight = 600;
      capturedImage.onload!();
    });

    expect(result.current.cardWidth).toBe('600px');
  });

  it('caps the width at 80 % of the container for a wide landscape image', () => {
    // 1600 × 600 → ratio 2.667 → naturalCardWidth = 600 × 2.667 = 1600
    // maxCardWidth = 1000 × 0.8 = 800  → min(1600, 800) = 800
    const post = makePost();
    const { result, rerender } = renderHook(({ p }) => useMediaCardWidth(p), {
      initialProps: { p: post },
    });
    attachContainer(result.current.containerRef, 1000, 600);

    rerender({ p: { ...post, id: 'post-wide' } });
    act(() => {
      capturedImage.naturalWidth = 1600;
      capturedImage.naturalHeight = 600;
      capturedImage.onload!();
    });

    expect(result.current.cardWidth).toBe('800px');
  });

  it('respects a custom maxWidthRatio', () => {
    // 800 × 400 → ratio 2 → naturalCardWidth = 400 × 2 = 800
    // maxCardWidth = 1000 × 0.5 = 500  → min(800, 500) = 500
    const post = makePost();
    const { result, rerender } = renderHook(({ p }) => useMediaCardWidth(p, 0.5), {
      initialProps: { p: post },
    });
    attachContainer(result.current.containerRef, 1000, 400);

    rerender({ p: { ...post, id: 'post-custom-ratio' } });
    act(() => {
      capturedImage.naturalWidth = 800;
      capturedImage.naturalHeight = 400;
      capturedImage.onload!();
    });

    expect(result.current.cardWidth).toBe('500px');
  });

  it('recomputes when the post changes', () => {
    const post1 = makePost({ id: 'post-a', image: 'https://cdn.example.com/a.jpg' });
    const post2 = makePost({ id: 'post-b', image: 'https://cdn.example.com/b.jpg' });

    // Start with a blank post (no image) so every subsequent rerender triggers
    // the effect (deps change because the id changes each time).
    const { result, rerender } = renderHook(({ p }) => useMediaCardWidth(p), {
      initialProps: { p: makePost({ id: 'initial', image: undefined }) },
    });
    attachContainer(result.current.containerRef, 1000, 600);

    // First post: portrait 400 × 600 → 400px
    rerender({ p: post1 });
    act(() => {
      capturedImage.naturalWidth = 400;
      capturedImage.naturalHeight = 600;
      capturedImage.onload!();
    });
    expect(result.current.cardWidth).toBe('400px');

    // Second post: landscape 1200 × 600 → capped at 800px
    rerender({ p: post2 });
    act(() => {
      capturedImage.naturalWidth = 1200;
      capturedImage.naturalHeight = 600;
      capturedImage.onload!();
    });
    expect(result.current.cardWidth).toBe('800px');
  });
});
