import { beforeEach, describe, expect, it, vi } from 'vitest';
import {
  createWatermark,
  deleteWatermark,
  findLogoBugWatermark,
  getWatermark,
  updateWatermark,
  watermarkImageUrl,
  type CreateWatermarkRequest
} from './watermarks';

function jsonResponse(body: unknown, status = 200): Response {
  return new Response(JSON.stringify(body), {
    headers: { 'Content-Type': 'application/json' },
    status
  });
}

function noContent(): Response {
  return new Response(null, { status: 204 });
}

const sampleRequest: CreateWatermarkRequest = {
  durationSeconds: 15,
  frequencyMinutes: 15,
  horizontalMargin: 5,
  image: 'abc.png',
  imageContentType: 'image/png',
  imageSource: 'Custom',
  location: 'BottomRight',
  mode: 'Permanent',
  name: 'Corner Logo',
  opacity: 100,
  opacityExpression: null,
  placeWithinSourceContent: false,
  size: 'Scaled',
  verticalMargin: 5,
  width: 15,
  zIndex: 0
};

describe('watermarks api client', () => {
  beforeEach(() => {
    window.localStorage.clear();
    vi.restoreAllMocks();
  });

  it('getWatermark fetches a watermark by id', async () => {
    const fetchMock = vi.spyOn(window, 'fetch').mockResolvedValue(jsonResponse({ id: 4, name: 'Corner Logo' }));

    await expect(getWatermark(4)).resolves.toMatchObject({ id: 4 });
    expect(fetchMock).toHaveBeenCalledWith('/api/v1/watermarks/4', expect.objectContaining({ method: 'GET' }));
  });

  it('createWatermark POSTs the request body', async () => {
    const fetchMock = vi.spyOn(window, 'fetch').mockResolvedValue(jsonResponse({ id: 7, name: 'Corner Logo' }, 201));

    await expect(createWatermark(sampleRequest)).resolves.toMatchObject({ id: 7 });

    const [url, init] = fetchMock.mock.calls[0];
    expect(url).toBe('/api/v1/watermarks');
    expect(init).toMatchObject({ method: 'POST' });
    expect(JSON.parse(String(init?.body)).mode).toBe('Permanent');
  });

  it('updateWatermark PUTs to the id route', async () => {
    const fetchMock = vi.spyOn(window, 'fetch').mockResolvedValue(jsonResponse({ id: 3, name: 'Corner Logo' }));

    await updateWatermark(3, sampleRequest);

    const [url, init] = fetchMock.mock.calls[0];
    expect(url).toBe('/api/v1/watermarks/3');
    expect(init).toMatchObject({ method: 'PUT' });
  });

  it('deleteWatermark issues a DELETE and resolves on 204', async () => {
    const fetchMock = vi.spyOn(window, 'fetch').mockResolvedValue(noContent());

    await expect(deleteWatermark(9)).resolves.toBeUndefined();
    expect(fetchMock).toHaveBeenCalledWith('/api/v1/watermarks/9', expect.objectContaining({ method: 'DELETE' }));
  });

  it('watermarkImageUrl builds a path-only URL (no reflected ?contentType=)', () => {
    // The serve route sniffs the stored file; the client no longer supplies a content type (#283).
    expect(watermarkImageUrl('abc.png')).toBe('/artwork/watermarks/abc.png');
  });
});

describe('findLogoBugWatermark', () => {
  it('prefers the row named "Channel Bug" over an earlier ChannelLogo row', () => {
    const watermarks = [
      { id: 4, imageSource: 'ChannelLogo' as const, name: 'Alpha Bug' },
      { id: 9, imageSource: 'ChannelLogo' as const, name: 'Channel Bug' }
    ];

    expect(findLogoBugWatermark(watermarks)).toMatchObject({ id: 9 });
  });

  it('falls back to the first ChannelLogo row when no "Channel Bug" exists', () => {
    const watermarks = [
      { id: 2, imageSource: 'Custom' as const, name: 'Corner bug' },
      { id: 4, imageSource: 'ChannelLogo' as const, name: 'Alpha Bug' },
      { id: 6, imageSource: 'ChannelLogo' as const, name: 'Beta Bug' }
    ];

    expect(findLogoBugWatermark(watermarks)).toMatchObject({ id: 4 });
  });

  it('returns null when none are ChannelLogo', () => {
    const watermarks = [
      { id: 2, imageSource: 'Custom' as const, name: 'Corner bug' },
      { id: 3, imageSource: 'Resource' as const, name: 'Station ident' }
    ];

    expect(findLogoBugWatermark(watermarks)).toBeNull();
  });
});
