import type {
  Exercise,
  TrainingSet,
  Session,
  SessionLog,
  LastLogResponse,
  ExerciseStats,
  CreateExerciseRequest,
  CreateSetRequest,
  UpdateSetRequest,
  CreateSessionRequest,
  CreateLogRequest,
  UpdateLogRequest,
} from '../types';

export class ApiError extends Error {
  constructor(
    public status: number,
    message: string,
  ) {
    super(message);
    this.name = 'ApiError';
  }
}

async function request<T>(
  url: string,
  options?: RequestInit,
): Promise<T> {
  const res = await fetch(url, {
    headers: { 'Content-Type': 'application/json' },
    ...options,
  });

  if (res.status === 204) {
    return undefined as T;
  }

  const data = await res.json();

  if (!res.ok) {
    throw new ApiError(res.status, data.error || 'Unbekannter Fehler');
  }

  return data as T;
}

export const api = {
  exercises: {
    list(muscleGroup?: string, q?: string): Promise<Exercise[]> {
      const params = new URLSearchParams();
      if (muscleGroup) params.set('muscle_group', muscleGroup);
      if (q) params.set('q', q);
      const qs = params.toString();
      return request<Exercise[]>(`/api/v1/exercises${qs ? '?' + qs : ''}`);
    },

    create(data: CreateExerciseRequest): Promise<Exercise> {
      return request<Exercise>('/api/v1/exercises', {
        method: 'POST',
        body: JSON.stringify(data),
      });
    },

    update(id: number, data: CreateExerciseRequest): Promise<Exercise> {
      return request<Exercise>(`/api/v1/exercises/${id}`, {
        method: 'PUT',
        body: JSON.stringify(data),
      });
    },

    delete(id: number): Promise<void> {
      return request<void>(`/api/v1/exercises/${id}`, {
        method: 'DELETE',
      });
    },

    lastLog(id: number): Promise<LastLogResponse> {
      return request<LastLogResponse>(`/api/v1/exercises/${id}/last-log`);
    },

    history(id: number, limit?: number): Promise<SessionLog[]> {
      const params = new URLSearchParams();
      if (limit) params.set('limit', String(limit));
      const qs = params.toString();
      return request<SessionLog[]>(
        `/api/v1/exercises/${id}/history${qs ? '?' + qs : ''}`,
      );
    },
  },

  sets: {
    list(): Promise<TrainingSet[]> {
      return request<TrainingSet[]>('/api/v1/sets');
    },

    create(data: CreateSetRequest): Promise<TrainingSet> {
      return request<TrainingSet>('/api/v1/sets', {
        method: 'POST',
        body: JSON.stringify(data),
      });
    },

    update(id: number, data: UpdateSetRequest): Promise<TrainingSet> {
      return request<TrainingSet>(`/api/v1/sets/${id}`, {
        method: 'PUT',
        body: JSON.stringify(data),
      });
    },

    delete(id: number): Promise<void> {
      return request<void>(`/api/v1/sets/${id}`, {
        method: 'DELETE',
      });
    },
  },

  sessions: {
    create(data: CreateSessionRequest): Promise<Session> {
      return request<Session>('/api/v1/sessions', {
        method: 'POST',
        body: JSON.stringify(data),
      });
    },

    list(limit?: number, offset?: number): Promise<Session[]> {
      const params = new URLSearchParams();
      if (limit) params.set('limit', String(limit));
      if (offset) params.set('offset', String(offset));
      const qs = params.toString();
      return request<Session[]>(`/api/v1/sessions${qs ? '?' + qs : ''}`);
    },

    get(id: number): Promise<Session> {
      return request<Session>(`/api/v1/sessions/${id}`);
    },

    end(id: number, note?: string): Promise<Session> {
      return request<Session>(`/api/v1/sessions/${id}/end`, {
        method: 'PUT',
        body: JSON.stringify({ note: note || '' }),
      });
    },

    createLog(sessionId: number, data: CreateLogRequest): Promise<SessionLog> {
      return request<SessionLog>(`/api/v1/sessions/${sessionId}/logs`, {
        method: 'POST',
        body: JSON.stringify(data),
      });
    },

    updateLog(
      sessionId: number,
      logId: number,
      data: UpdateLogRequest,
    ): Promise<SessionLog> {
      return request<SessionLog>(
        `/api/v1/sessions/${sessionId}/logs/${logId}`,
        {
          method: 'PUT',
          body: JSON.stringify(data),
        },
      );
    },

    deleteLog(sessionId: number, logId: number): Promise<void> {
      return request<void>(`/api/v1/sessions/${sessionId}/logs/${logId}`, {
        method: 'DELETE',
      });
    },
  },

  stats: {
    overview(): Promise<ExerciseStats[]> {
      return request<ExerciseStats[]>('/api/v1/stats/overview');
    },
  },
};
