import { describe, expect, it, vi } from 'vitest';

import { PicloudClient, PicloudHttpError } from '../src/index.js';
import { headerOf, jsonResponse, lastInit, lastUrl, type FetchArgs } from './helpers.js';

describe('endpoint', () => {
  it('post round-trips a typed request/response', async () => {
    const fetchMock = vi.fn(async (_u: FetchArgs[0], _i?: FetchArgs[1]) =>
      jsonResponse({ id: '1', name: 'alice', created_at: 'now' }, 201)
    );
    const client = new PicloudClient({ baseURL: 'https://api.test', fetch: fetchMock });

    interface Req {
      name: string;
      role: string;
    }
    interface Res {
      id: string;
      name: string;
      created_at: string;
    }
    const res = await client.endpoint<Req, Res>('/api/users').post({ name: 'alice', role: 'admin' });

    expect(res).toEqual({ id: '1', name: 'alice', created_at: 'now' });
    expect(lastUrl(fetchMock)).toBe('https://api.test/api/users');
    const init = lastInit(fetchMock);
    expect(init.method).toBe('POST');
    expect(JSON.parse(String(init.body))).toEqual({ name: 'alice', role: 'admin' });
    expect(headerOf(init, 'Content-Type')).toBe('application/json');
  });

  it('get round-trips', async () => {
    const fetchMock = vi.fn(async (_u: FetchArgs[0], _i?: FetchArgs[1]) =>
      jsonResponse({ name: 'bob' })
    );
    const client = new PicloudClient({ baseURL: 'https://api.test', fetch: fetchMock });
    const res = await client.endpoint<unknown, { name: string }>('/api/users/1').get();
    expect(res.name).toBe('bob');
    expect(lastInit(fetchMock).method).toBe('GET');
  });

  it('injects the auth token from getAuthToken', async () => {
    const fetchMock = vi.fn(async (_u: FetchArgs[0], _i?: FetchArgs[1]) => jsonResponse({ ok: true }));
    const client = new PicloudClient({
      baseURL: 'https://api.test',
      fetch: fetchMock,
      getAuthToken: () => 'tok-123'
    });
    await client.endpoint('/api/me').get();
    expect(headerOf(lastInit(fetchMock), 'Authorization')).toBe('Bearer tok-123');
  });

  it('throws PicloudHttpError with status + body on non-2xx', async () => {
    const fetchMock = vi.fn(async (_u: FetchArgs[0], _i?: FetchArgs[1]) =>
      jsonResponse({ error: 'bad input' }, 422)
    );
    const client = new PicloudClient({ baseURL: 'https://api.test', fetch: fetchMock });
    const err = await client
      .endpoint('/api/x')
      .get()
      .catch((e: unknown) => e);
    expect(err).toBeInstanceOf(PicloudHttpError);
    expect((err as PicloudHttpError).status).toBe(422);
    expect((err as PicloudHttpError).message).toBe('bad input');
  });

  it('applies an optional validator to the response', async () => {
    const fetchMock = vi.fn(async (_u: FetchArgs[0], _i?: FetchArgs[1]) =>
      jsonResponse({ id: 7 })
    );
    const client = new PicloudClient({ baseURL: 'https://api.test', fetch: fetchMock });
    const validator = {
      parse: (input: unknown) => {
        const r = input as { id: number };
        if (typeof r.id !== 'number') throw new Error('bad');
        return r;
      }
    };
    const res = await client.endpoint<unknown, { id: number }>('/api/x').get({ validate: validator });
    expect(res.id).toBe(7);
  });
});
