/* ============================================================
   BAN YARO — API Client
   Zentraler Eingang für ALLE Backend-Kommunikation.
   Kein fetch() wird außerhalb dieser Datei aufgerufen.
   ============================================================ */

const API = (() => {

  // ----------------------------------------------------------
  // Request-Deduplication: gleiche GET-URL nur einmal in-flight
  // ----------------------------------------------------------
  const _inflight = new Map();

  // ----------------------------------------------------------
  // Interner HTTP-Kern
  // ----------------------------------------------------------
  async function _doRequest(method, path, body, attempt) {
    const config = {
      method,
      headers: { 'Content-Type': 'application/json' },
      credentials: 'include',
    };

    if (body && !(body instanceof FormData)) {
      config.body = JSON.stringify(body);
    } else if (body instanceof FormData) {
      delete config.headers['Content-Type'];
      config.body = body;
    }

    const token = localStorage.getItem('by_token');
    if (token) config.headers['Authorization'] = `Bearer ${token}`;

    let response;
    try {
      response = await fetch(`/api${path}`, config);
    } catch {
      // Netzwerkfehler: bei GET bis zu 2 Retry-Versuche
      if (method === 'GET' && attempt < 2) {
        await new Promise(r => setTimeout(r, 200 * Math.pow(3, attempt)));
        return _doRequest(method, path, body, attempt + 1);
      }
      const msg = 'Kein Internet — du bist offline.';
      if (window.UI?.toast) UI.toast.warning(msg, 4000);
      throw new APIError(msg, 0, 'network');
    }

    if (response.status === 204) return null;

    let data;
    try { data = await response.json(); } catch { data = null; }

    if (!response.ok) {
      const message = data?.detail || data?.message || `Fehler ${response.status}`;
      const isSwOffline = response.status === 503 && message.startsWith('Offline');

      // Retry: GET auf echte 5xx (nicht SW-generierte Offline-503)
      if (method === 'GET' && response.status >= 500 && !isSwOffline && attempt < 2) {
        await new Promise(r => setTimeout(r, 200 * Math.pow(3, attempt)));
        return _doRequest(method, path, body, attempt + 1);
      }

      if (isSwOffline && window.UI?.toast) UI.toast.warning('Kein Internet — du bist offline.', 4000);
      throw new APIError(message, response.status, isSwOffline ? 'network' : data?.code);
    }

    if (data?._queued) {
      if (typeof UI !== 'undefined' && UI.toast)
        UI.toast.info('Offline gespeichert — wird automatisch synchronisiert');
      return data;
    }

    return data;
  }

  async function _request(method, path, body = null) {
    // GET-Deduplication: laufende identische Anfragen zusammenfassen
    if (method === 'GET') {
      if (_inflight.has(path)) return _inflight.get(path);
      const promise = _doRequest('GET', path, null, 0).finally(() => _inflight.delete(path));
      _inflight.set(path, promise);
      return promise;
    }
    return _doRequest(method, path, body, 0);
  }

  // ----------------------------------------------------------
  // Öffentliche HTTP-Methoden
  // ----------------------------------------------------------
  const get    = (path)         => _request('GET',    path);
  const post   = (path, body)   => _request('POST',   path, body);
  const put    = (path, body)   => _request('PUT',    path, body);
  const patch  = (path, body)   => _request('PATCH',  path, body);
  const del    = (path)         => _request('DELETE', path);
  const upload = (path, form)   => _request('POST',   path, form); // FormData

  // ----------------------------------------------------------
  // AUTH
  // ----------------------------------------------------------
  const auth = {
    login(email, password) {
      return post('/auth/login', { email, password });
    },
    register(email, password, name, ref_code) {
      const body = { email, password, name };
      if (ref_code) body.ref_code = ref_code;
      return post('/auth/register', body);
    },
    logout() {
      localStorage.removeItem('by_token');
      return post('/auth/logout');
    },
    me() {
      return get('/auth/me');
    },
    referral: () => get('/auth/referral'),
  };

  // ----------------------------------------------------------
  // HUNDE-PROFIL
  // ----------------------------------------------------------
  const dogs = {
    list()           { return get('/dogs'); },
    get(id)          { return get(`/dogs/${id}`); },
    create(data)     { return post('/dogs', data); },
    update(id, data) { return patch(`/dogs/${id}`, data); },
    delete(id)       { return del(`/dogs/${id}`); },
    uploadPhoto(id, formData) { return upload(`/dogs/${id}/photo`, formData); },
    updatePhotoPosition(id, zoom, offsetX, offsetY) {
      return patch(`/dogs/${id}/photo-position`, { zoom, offset_x: offsetX, offset_y: offsetY });
    },
    deletePhoto(id) { return del(`/dogs/${id}/photo`); },
    getSkills(id)   { return get(`/dogs/${id}/skills`); },
    welcomeDashboard(dogId) { return get(`/dogs/${dogId}/welcome-dashboard`); },
  };

  // ----------------------------------------------------------
  // TAGEBUCH
  // ----------------------------------------------------------
  const diary = {
    list(dogId, params = {}) {
      const q = new URLSearchParams(params).toString();
      return get(`/dogs/${dogId}/diary${q ? '?' + q : ''}`);
    },
    stats(dogId)           { return get(`/dogs/${dogId}/diary/stats`); },
    get(dogId, entryId)    { return get(`/dogs/${dogId}/diary/${entryId}`); },
    create(dogId, data)    { return post(`/dogs/${dogId}/diary`, data); },
    update(dogId, id, data){ return patch(`/dogs/${dogId}/diary/${id}`, data); },
    delete(dogId, id)      { return del(`/dogs/${dogId}/diary/${id}`); },
    uploadMedia(dogId, id, formData) {
      return upload(`/dogs/${dogId}/diary/${id}/media`, formData);
    },
    deleteMedia(dogId, id) {
      return del(`/dogs/${dogId}/diary/${id}/media`);
    },
    deleteMediaItem(dogId, entryId, mediaId) {
      return del(`/dogs/${dogId}/diary/${entryId}/media/${mediaId}`);
    },
    setCover(dogId, entryId, mediaId) {
      return patch(`/dogs/${dogId}/diary/${entryId}/media/${mediaId}/cover`, {});
    },
    nearby(dogId, lat, lon) {
      return get(`/dogs/${dogId}/diary/nearby?lat=${lat}&lon=${lon}`);
    },
    locations(dogId) { return get(`/dogs/${dogId}/diary/locations`); },
    calendar(dogId)  { return get(`/dogs/${dogId}/diary/calendar`); },
  };

  // ----------------------------------------------------------
  // GESUNDHEIT
  // ----------------------------------------------------------
  const health = {
    list(dogId, typ = null) {
      const q = typ ? `?typ=${encodeURIComponent(typ)}` : '';
      return get(`/dogs/${dogId}/health${q}`);
    },
    create(dogId, data)   { return post(`/dogs/${dogId}/health`, data); },
    update(dogId, id, d)  { return patch(`/dogs/${dogId}/health/${id}`, d); },
    delete(dogId, id)     { return del(`/dogs/${dogId}/health/${id}`); },
    uploadDokument(dogId, id, formData) {
      return upload(`/dogs/${dogId}/health/${id}/dokument`, formData);
    },
    deleteDocument(dogId, id) {
      return del(`/dogs/${dogId}/health/${id}/dokument`);
    },
    uploadMedia(dogId, entryId, formData) {
      return upload(`/dogs/${dogId}/health/${entryId}/media`, formData);
    },
    deleteMedia(dogId, entryId, mediaId) {
      return del(`/dogs/${dogId}/health/${entryId}/media/${mediaId}`);
    },
    kiZusammenfassung(dogId) {
      return post(`/dogs/${dogId}/health/ki-zusammenfassung`);
    },
    kiBerichte(dogId)         { return get(`/dogs/${dogId}/health/ki-berichte`); },
    terminvorschlaege(dogId)  { return get(`/dogs/${dogId}/health/terminvorschlaege`); },
    symptomCheck(dogId, symptoms) {
      return post(`/dogs/${dogId}/health/symptom-check`, { symptoms });
    },
    gewichtVerlauf(dogId) {
      return get(`/dogs/${dogId}/health/gewicht`);
    },
  };

  // ----------------------------------------------------------
  // TIERÄRZTE
  // ----------------------------------------------------------
  const tieraerzte = {
    list()              { return get('/tieraerzte'); },
    create(data)        { return post('/tieraerzte', data); },
    update(id, d)       { return patch(`/tieraerzte/${id}`, d); },
    osmNearby(lat, lon) { return get(`/tieraerzte/osm-nearby?lat=${lat}&lon=${lon}`); },
    myFavorite()        { return get('/tieraerzte/my-favorite'); },
    toggleFavorite(id)  { return post(`/tieraerzte/${id}/favorite`); },
  };

  // ----------------------------------------------------------
  // GESUNDHEITSDOKUMENTE
  // ----------------------------------------------------------
  const healthDocs = {
    list(dogId)          { return get(`/health-docs?dog_id=${dogId}`); },
    upload(formData)     { return upload('/health-docs/upload', formData); },
    delete(id)           { return del(`/health-docs/${id}`); },
  };

  // ----------------------------------------------------------
  // GIFTKÖDER-ALARM
  // ----------------------------------------------------------
  const poison = {
    listNearby(lat, lon, radius = 5000) {
      return get(`/poison?lat=${lat}&lon=${lon}&radius=${radius}`);
    },
    report(data)         { return post('/poison', data); },
    confirm(id)          { return post(`/poison/${id}/confirm`); },
    resolve(id, data={}) { return post(`/poison/${id}/resolve`, data); },
    uploadPhoto(id, form){ return upload(`/poison/${id}/photo`, form); },
  };

  // ----------------------------------------------------------
  // KARTE & ORTE
  // ----------------------------------------------------------
  const places = {
    list(typ = null) {
      return get(`/places${typ ? '?typ=' + encodeURIComponent(typ) : ''}`);
    },
    listNearby(lat, lon, typ = null, radius = 5000) {
      const params = new URLSearchParams({ lat, lon, radius });
      if (typ) params.set('typ', typ);
      return get(`/places?${params}`);
    },
    get(id)           { return get(`/places/${id}`); },
    create(data)      { return post('/places', data); },
    update(id, data)  { return patch(`/places/${id}`, data); },
    delete(id)        { return del(`/places/${id}`); },
  };

  // ----------------------------------------------------------
  // GASSI-ROUTEN
  // ----------------------------------------------------------
  const routes = {
    list() {
      return get('/routes');
    },
    listNearby(lat, lon, radius = 10000) {
      return get(`/routes?lat=${lat}&lon=${lon}&radius=${radius}`);
    },
    get(id)          { return get(`/routes/${id}`); },
    create(data)     { return post('/routes', data); },
    update(id, data) { return patch(`/routes/${id}`, data); },
    trim(id, gps_track)    { return patch(`/routes/${id}/trim`, { gps_track }); },
    feedback(id, text)     { return post(`/routes/${id}/feedback`, { text }); },
    elevation(id)          { return get(`/routes/${id}/elevation`); },
    delete(id)             { return del(`/routes/${id}`); },
    rate(id, wertung)                       { return post(`/routes/${id}/rate`, { wertung }); },
    walked(id, walked_km, progress_pct)    { return post(`/routes/${id}/walked`, { walked_km, progress_pct }); },
    reverse(id)                            { return post(`/routes/${id}/reverse`, {}); },
    addPhoto(id, file) {
      const fd = new FormData();
      fd.append('file', file);
      return fetch(`/api/routes/${id}/photo`, {
        method: 'POST', credentials: 'include', body: fd,
      }).then(r => r.ok ? r.json() : Promise.reject(new Error('Foto-Upload fehlgeschlagen')));
    },
  };

  // ----------------------------------------------------------
  // TRAINING & ÜBUNGSFORTSCHRITT
  // ----------------------------------------------------------
  const training = {
    getProgress()            { return get('/training/progress'); },
    setProgress(id, status)  { return post('/training/progress', { exercise_id: id, status }); },
    getSuggestions()         { return get('/training/suggestions'); },
    getPlanProgress()        { return get('/training/plan-progress'); },
    setPlanProgress(key, checked) { return post('/training/plan-progress', { item_key: key, checked }); },
    getRecommendations(dogId) { return get(`/training/recommendations?dog_id=${dogId}`); },
  };

  // ----------------------------------------------------------
  // GASSI-TREFFEN
  // ----------------------------------------------------------
  const walks = {
    list(lat = null, lon = null, radius = 20000) {
      const params = new URLSearchParams({ radius });
      if (lat !== null) { params.set('lat', lat); params.set('lon', lon); }
      return get(`/walks?${params}`);
    },
    get(id)              { return get(`/walks/${id}`); },
    create(data)         { return post('/walks', data); },
    update(id, data)     { return patch(`/walks/${id}`, data); },
    cancel(id)           { return del(`/walks/${id}`); },
    join(id, dogIds)          { return post(`/walks/${id}/join`, { dog_ids: dogIds }); },
    leave(id)                 { return del(`/walks/${id}/join`); },
    nearby(lat, lon)          { return get(`/walks/nearby?lat=${lat}&lon=${lon}`); },
    inviteCandidates(id)      { return get(`/walks/${id}/invite-candidates`); },
    invite(id, friendId)      { return post(`/walks/${id}/invite`, { friend_id: friendId }); },
    rsvp(id, status)          { return post(`/walks/${id}/rsvp`, { status }); },
    participants(id)          { return get(`/walks/${id}/participants`); },
  };

  // ----------------------------------------------------------
  // EVENTS
  // ----------------------------------------------------------
  const events = {
    list(params = {}) {
      const q = new URLSearchParams(params).toString();
      return get(`/events${q ? '?' + q : ''}`);
    },
    get(id)              { return get(`/events/${id}`); },
    create(data)         { return post('/events', data); },
    update(id, data)     { return patch(`/events/${id}`, data); },
    delete(id)           { return del(`/events/${id}`); },
    rsvp(id, status)     { return post(`/events/${id}/rsvp`, { status }); },
    cancelRsvp(id)       { return del(`/events/${id}/rsvp`); },
    listRsvp(id)         { return get(`/events/${id}/rsvp`); },
  };

  // ----------------------------------------------------------
  // SITTING
  // ----------------------------------------------------------
  const sitting = {
    list(lat = null, lon = null, radius = 30000, service = null) {
      const p = new URLSearchParams({ radius });
      if (lat !== null) { p.set('lat', lat); p.set('lon', lon); }
      if (service)      { p.set('service', service); }
      return get(`/sitting?${p}`);
    },
    me()             { return get('/sitting/me'); },
    create(data)     { return post('/sitting', data); },
    updateMe(data)   { return patch('/sitting/me', data); },
    requests()       { return get('/sitting/requests'); },
    inbox()          { return get('/sitting/inbox'); },
    sendRequest(data){ return post('/sitting/requests', data); },
    updateRequest(id, status) { return patch(`/sitting/requests/${id}`, { status }); },
  };

  // ----------------------------------------------------------
  // RATINGS
  // ----------------------------------------------------------
  const ratings = {
    rate(targetType, targetId, stars, kommentar = null) {
      return post('/ratings', { target_type: targetType, target_id: targetId, stars, kommentar });
    },
    list(targetType, targetId) {
      return get(`/ratings/${targetType}/${targetId}`);
    },
    mine(targetType, targetId) {
      return get(`/ratings/me/${targetType}/${targetId}`);
    },
  };

  // ----------------------------------------------------------
  // FORUM
  // ----------------------------------------------------------
  const forum = {
    threads(params = {}) {
      const q = new URLSearchParams(params).toString();
      return get(`/forum/threads${q ? '?' + q : ''}`);
    },
    thread(id)             { return get(`/forum/threads/${id}`); },
    create(data)           { return post('/forum/threads', data); },
    deleteThread(id)       { return del(`/forum/threads/${id}`); },
    patchThread(id, data)  { return patch(`/forum/threads/${id}`, data); },
    addPost(threadId, data){ return post(`/forum/threads/${threadId}/posts`, data); },
    deletePost(id)         { return del(`/forum/posts/${id}`); },
    updateThread(id, data) { return patch(`/forum/threads/${id}/content`, data); },
    updatePost(id, data)   { return patch(`/forum/posts/${id}`, data); },
    uploadThreadFoto(id, file) {
      const fd = new FormData(); fd.append('file', file);
      return upload(`/forum/threads/${id}/fotos`, fd);
    },
    uploadPostFoto(id, file) {
      const fd = new FormData(); fd.append('file', file);
      return upload(`/forum/posts/${id}/fotos`, fd);
    },
    like(targetType, targetId) {
      return post('/forum/like', { target_type: targetType, target_id: targetId });
    },
    report(targetType, targetId, grund) {
      return post('/forum/report', { target_type: targetType, target_id: targetId, grund });
    },
    reports()              { return get('/forum/reports'); },
    resolveReport(id)      { return patch(`/forum/reports/${id}`, { resolved: 1 }); },
    membersMap()           { return get('/forum/members/map'); },
    setLocation(lat, lon, show) {
      return patch('/forum/members/location', { lat, lon, show });
    },
    search(q)              { return get(`/forum/search?q=${encodeURIComponent(q)}`); },

    // Legacy aliases (keep old names working)
    listThreads(params = {}) { return forum.threads(params); },
    getThread(id)            { return forum.thread(id); },
    createThread(data)       { return forum.create(data); },
    createPost(tid, data)    { return forum.addPost(tid, data); },
  };

  // ----------------------------------------------------------
  // VERLORENER HUND
  // ----------------------------------------------------------
  const lost = {
    list(lat = null, lon = null, radius_km = 25) {
      const params = new URLSearchParams({ radius_km });
      if (lat !== null) { params.set('lat', lat); params.set('lon', lon); }
      return get(`/lost?${params}`);
    },
    report(data)          { return post('/lost', data); },
    uploadFoto(id, form)  { return upload(`/lost/${id}/foto`, form); },
    markFound(id)         { return post(`/lost/${id}/found`); },
    delete(id)            { return del(`/lost/${id}`); },
  };

  // ----------------------------------------------------------
  // KNIGGE
  // ----------------------------------------------------------
  const knigge = {
    vote:  (szenario_id, answer) => post('/knigge/vote', { szenario_id, answer }),
    votes: (szenario_id)         => get(`/knigge/votes?szenario_id=${encodeURIComponent(szenario_id)}`),
    kiRat: (situation)           => post('/knigge/ki-rat', { situation }),
  };

  // ----------------------------------------------------------
  // WETTER
  // ----------------------------------------------------------
  const weather = {
    alerts(lat, lon)   { return get(`/weather/alerts?lat=${lat}&lon=${lon}`); },
    get(lat, lon)      { return get(`/weather?lat=${lat}&lon=${lon}`); },
    forecast(lat, lon) { return get(`/weather/forecast?lat=${lat}&lon=${lon}`); },
  };

  // ----------------------------------------------------------
  // FREUNDE
  // ----------------------------------------------------------
  const friends = {
    list()                   { return get('/friends/'); },
    search(q)                { return get(`/friends/search?q=${encodeURIComponent(q)}`); },
    activity()               { return get('/friends/activity'); },
    sendRequest(userId)      { return post(`/friends/request/${userId}`, {}); },
    accept(friendshipId)     { return post(`/friends/${friendshipId}/accept`, {}); },
    decline(friendshipId)    { return post(`/friends/${friendshipId}/decline`, {}); },
    remove(friendUserId)     { return del(`/friends/${friendUserId}`); },
  };

  // ----------------------------------------------------------
  // DIREKTNACHRICHTEN
  // ----------------------------------------------------------
  const chat = {
    conversations()             { return get('/chat/conversations'); },
    start(partnerId)            { return post('/chat/conversations', { partner_id: partnerId }); },
    messages(convId, offset=0)  { return get(`/chat/conversations/${convId}?offset=${offset}`); },
    send(convId, text)          { return post(`/chat/conversations/${convId}/messages`, { text }); },
    markRead(convId)            { return post(`/chat/conversations/${convId}/read`, {}); },
    deleteMessage(msgId)        { return del(`/chat/messages/${msgId}`); },
    uploadPhoto(convId, file)   {
      const fd = new FormData();
      fd.append('file', file);
      return upload(`/chat/conversations/${convId}/upload`, fd);
    },
    heartbeat()                 { return post('/chat/heartbeat', {}); },
  };

  // ----------------------------------------------------------
  // PUSH NOTIFICATIONS
  // ----------------------------------------------------------
  const push = {
    getVapidKey()           { return get('/push/vapid-key'); },
    subscribe(subscription) { return post('/push/subscribe', subscription); },
    unsubscribe()           { return del('/push/subscribe'); },
  };

  // ----------------------------------------------------------
  // PUSH-SUBSCRIPTION HELPER
  // ----------------------------------------------------------
  async function subscribeToPush() {
    if (!('serviceWorker' in navigator) || !('PushManager' in window)) return null;

    const permission = await Notification.requestPermission();
    if (permission !== 'granted') return null;

    const { vapid_public_key } = await push.getVapidKey();
    const reg = await navigator.serviceWorker.ready;

    const subscription = await reg.pushManager.subscribe({
      userVisibleOnly:      true,
      applicationServerKey: _urlBase64ToUint8Array(vapid_public_key),
    });

    await push.subscribe(subscription.toJSON());
    return subscription;
  }

  function _urlBase64ToUint8Array(base64String) {
    const padding = '='.repeat((4 - base64String.length % 4) % 4);
    const base64  = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
    const raw     = atob(base64);
    return new Uint8Array([...raw].map(c => c.charCodeAt(0)));
  }

  // ----------------------------------------------------------
  // GEOLOCATION HELPER
  // ----------------------------------------------------------
  function getLocation(options = {}) {
    return new Promise((resolve, reject) => {
      if (!navigator.geolocation) {
        reject(new Error('Geolocation wird nicht unterstützt.'));
        return;
      }
      navigator.geolocation.getCurrentPosition(
        pos => resolve({ lat: pos.coords.latitude, lon: pos.coords.longitude }),
        err => reject(err),
        { timeout: 8000, maximumAge: 60000, ...options }
      );
    });
  }

  // ----------------------------------------------------------
  // WEBCAL
  // ----------------------------------------------------------
  const webcal = {
    getToken:    () => get('/webcal/token'),
    resetToken:  () => del('/webcal/token'),
  };

  const sharing = {
    create: (dogId, role) => post(`/dogs/${dogId}/share`, { role }),
    list:   (dogId)       => get(`/dogs/${dogId}/shares`),
    revoke: (dogId, id)   => del(`/dogs/${dogId}/share/${id}`),
    accept: (token)       => post(`/share/accept/${token}`, {}),
    info:   (token)       => get(`/share/info/${token}`),
  };

  const widget = {
    snapshot: () => get('/widget/snapshot'),
  };

  // ----------------------------------------------------------
  // NOTIFICATIONS
  // ----------------------------------------------------------
  const notifications = {
    list()        { return get('/notifications'); },
    unreadCount() { return get('/notifications/unread-count'); },
    badge()       { return get('/notifications/badge'); },
    readAll()     { return patch('/notifications/read-all', {}); },
    read(id)      { return patch(`/notifications/${id}/read`, {}); },
    delete(id)    { return del(`/notifications/${id}`); },
  };

  // ----------------------------------------------------------
  // SERVICE-ANGEBOTE (Sitting & Walks Matching)
  // ----------------------------------------------------------
  const services = {
    list(type, lat = null, lon = null, radius = 20) {
      const p = new URLSearchParams({ type, radius });
      if (lat !== null) { p.set('lat', lat); p.set('lon', lon); }
      return get(`/services?${p}`);
    },
    me()             { return get('/services/me'); },
    upsert(data)     { return post('/services', data); },
    deactivate(id)   { return del(`/services/${id}`); },
  };

  // ----------------------------------------------------------
  // GASTHUND-ZUGANG
  // ----------------------------------------------------------
  const sittingAccess = {
    grant:  (data) => post('/sitting-access', data),
    revoke: (id)   => del(`/sitting-access/${id}`),
    my:     ()     => get('/sitting-access/my'),
  };

  const importData = {
    notestation(dogId, file) {
      const fd = new FormData();
      fd.append('dog_id', dogId);
      fd.append('file', file);
      return upload('/import/notestation', fd);
    },
    csv(dogId, file) {
      const fd = new FormData();
      fd.append('dog_id', dogId);
      fd.append('file', file);
      return upload('/import/csv', fd);
    },
  };

  // ----------------------------------------------------------
  // NOTIZEN
  // ----------------------------------------------------------
  const notes = {
    get(parentType, parentId) {
      return get(`/notes/${parentType}/${parentId}`);
    },
    getAll(params) {
      return get('/notes?' + new URLSearchParams(params || {}).toString());
    },
    analyse() {
      return post('/notes/ki-analyse', {});
    },
    create(parentType, parentId, data) {
      return post(`/notes/${parentType}/${parentId}`, data);
    },
    update(id, data) {
      return patch(`/notes/${id}`, data);
    },
    delete(id) {
      return del(`/notes/${id}`);
    },
  };

  // ----------------------------------------------------------
  // ERROR-KLASSE
  // ----------------------------------------------------------
  class APIError extends Error {
    constructor(message, status, code) {
      super(message);
      this.name   = 'APIError';
      this.status = status;
      this.code   = code;
    }
  }

  // Lokale Gerätezeit als ISO-String ("2026-04-26T12:00:00") für server-seitige created_at
  function clientNow() {
    return new Date().toLocaleString('sv').replace(' ', 'T');
  }

  // ----------------------------------------------------------
  // ZÜCHTER
  // ----------------------------------------------------------
  const breeder = {
    status()                        { return get('/breeder/status'); },
    apply(form)                     { return upload('/breeder/apply', form); },
    profile(zwingername)            { return get(`/breeder/profil/${encodeURIComponent(zwingername)}`); },
    mapMarkers()                    { return get('/breeder/map'); },
    updateProfile(data)             { return put('/breeder/profile', data); },
    adminCreateProfile()            { return post('/admin/breeder/create-profile', {}); },
    pendingList()                   { return get('/admin/breeders/pending'); },
    documents(userId)               { return get(`/admin/breeder/${userId}/documents`); },
    documentUrl(userId, docId)      { return `/api/admin/breeder/${userId}/document/${docId}`; },
    approve(userId)                 { return post(`/admin/breeder/${userId}/approve`, {}); },
    reject(userId, grund)           { return post(`/admin/breeder/${userId}/reject`, { grund }); },
  };

  // ----------------------------------------------------------
  // WÜRFE (Züchter-Wurf-Verwaltung)
  // ----------------------------------------------------------
  const litters = {
    // Züchter: eigene Würfe
    myList()              { return get('/litters/my'); },
    create(data)          { return post('/litters', data); },
    update(id, data)      { return put(`/litters/${id}`, data); },
    remove(id)            { return del(`/litters/${id}`); },
    welfareConfirm(id)    { return post(`/litters/${id}/welfare-confirm`, {}); },
    // Welpen
    puppies(id)           { return get(`/litters/${id}/puppies`); },
    addPuppy(id, data)    { return post(`/litters/${id}/puppies`, data); },
    updatePuppy(id, data) { return put(`/litters/puppies/${id}`, data); },
    addWeight(id, data)   { return post(`/litters/puppies/${id}/weight`, data); },
    // Öffentlich
    public(params)        { return get('/litters?' + new URLSearchParams(params || {}).toString()); },
    detail(id)            { return get(`/litters/${id}`); },
  };

  // ----------------------------------------------------------
  // ZÜCHTER-FOTOS
  // ----------------------------------------------------------
  const breederPhotos = {
    upload(form)                         { return upload('/breeder/photos/upload', form); },
    list(entityType, entityId)           { return get(`/photos/${entityType}/${entityId}`); },
    updateVisibility(id, visibility)     { return patch(`/breeder/photos/${id}/visibility`, { visibility }); },
    setPrimary(id)                       { return patch(`/breeder/photos/${id}/primary`, {}); },
    updateCaption(id, caption)           { return patch(`/breeder/photos/${id}/caption`, { caption }); },
    remove(id)                           { return del(`/breeder/photos/${id}`); },
  };

  // ----------------------------------------------------------
  // ZUCHTKARTEI (Hunde-Stammdaten, Gesundheit, Genetik, Titel)
  // ----------------------------------------------------------
  const zuchthunde = {
    list()               { return get('/zuchthunde'); },
    get(id)              { return get(`/zuchthunde/${id}`); },
    create(data)         { return post('/zuchthunde', data); },
    update(id, data)     { return put(`/zuchthunde/${id}`, data); },
    remove(id)           { return del(`/zuchthunde/${id}`); },
    pedigree(id, gen=4)  { return get(`/zuchthunde/${id}/pedigree?generations=${gen}`); },
    healthTests(id)                   { return get(`/zuchthunde/${id}/health-tests`); },
    addHealthTest(id, data)           { return post(`/zuchthunde/${id}/health-tests`, data); },
    updateHealthTest(tid, data)       { return put(`/zuchthunde/health-tests/${tid}`, data); },
    deleteHealthTest(tid)             { return del(`/zuchthunde/health-tests/${tid}`); },
    geneticTests(id)                  { return get(`/zuchthunde/${id}/genetic-tests`); },
    addGeneticTest(id, data)          { return post(`/zuchthunde/${id}/genetic-tests`, data); },
    updateGeneticTest(tid, data)      { return put(`/zuchthunde/genetic-tests/${tid}`, data); },
    deleteGeneticTest(tid)            { return del(`/zuchthunde/genetic-tests/${tid}`); },
    titles(id)                        { return get(`/zuchthunde/${id}/titles`); },
    addTitle(id, data)                { return post(`/zuchthunde/${id}/titles`, data); },
    updateTitle(tid, data)            { return put(`/zuchthunde/titles/${tid}`, data); },
    deleteTitle(tid)                  { return del(`/zuchthunde/titles/${tid}`); },
    trialMating(vaterId, mutterId)    { return post('/zuchthunde/trial-mating', { vater_id: vaterId, mutter_id: mutterId }); },
  };

  // ----------------------------------------------------------
  // ZÜCHTER-KI
  // ----------------------------------------------------------
  const zuchtKi = {
    wurfankuendigung(litterId)          { return post('/zucht-ki/wurfankuendigung', { litter_id: litterId }); },
    genetikErklaerung(litterId, ziel)   { return post('/zucht-ki/genetik-erklaerung', { litter_id: litterId, zielgruppe: ziel }); },
    paarungAnalyse(vaterId, mutterId, ik, welfareLevel) {
      return post('/zucht-ki/paarung-analyse', { vater_id: vaterId, mutter_id: mutterId, ik_prozent: ik, welfare_level: welfareLevel });
    },
    hundBeschreibung(hundId)            { return post('/zucht-ki/hund-beschreibung', { hund_id: hundId }); },
    jahresbericht()                     { return post('/zucht-ki/jahresbericht', {}); },
    jahresberichtList()                 { return get('/zucht-ki/jahresbericht'); },
    jahresberichtGet(id)                { return get(`/zucht-ki/jahresbericht/${id}`); },
  };

  const osm = {
    pois: (type, south, west, north, east) =>
      get(`/osm/pois?type=${type}&south=${south}&west=${west}&north=${north}&east=${east}&fast=true`),
  };

  // Öffentliche API
  return {
    get, post, put, patch, del, upload,
    auth, dogs, diary, health, tieraerzte, healthDocs, poison,
    places, routes, walks, events, sitting, forum, lost, knigge, weather, push,
    friends, chat, webcal, importData, sharing, widget, notifications, services, ratings, sittingAccess, training, notes,
    breeder, litters, breederPhotos, zuchthunde, zuchtKi,
    osm,
    subscribeToPush, getLocation, clientNow,
    APIError,
  };

})();
