/**
 * TASKMATE - API Client
 * =====================
 */

class ApiClient {
  constructor() {
    this.baseUrl = '/api';
    this.token = null;
    this.refreshToken = null;
    this.csrfToken = null;
    this.refreshingToken = false;
    this.requestQueue = [];
    this.refreshTimer = null;
    this.init();
  }

  init() {
    // Token aus Storage laden
    this.token = localStorage.getItem('auth_token');
    this.refreshToken = localStorage.getItem('refresh_token');
    this.csrfToken = sessionStorage.getItem('csrf_token');
    console.log('[API] init() - Token loaded:', this.token ? this.token.substring(0, 20) + '...' : 'NULL');
    
    // Starte Timer wenn Token und Refresh-Token vorhanden sind
    if (this.token && this.refreshToken) {
      this.startTokenRefreshTimer();
    }
  }

  // Token Management
  setToken(token) {
    console.log('[API] setToken:', token ? token.substring(0, 20) + '...' : 'NULL');
    this.token = token;
    if (token) {
      localStorage.setItem('auth_token', token);
      // Starte proaktiven Token-Refresh Timer (nach 10 Minuten)
      this.startTokenRefreshTimer();
    } else {
      this.token = null;
      localStorage.removeItem('auth_token');
      localStorage.removeItem('current_user');
      this.clearTokenRefreshTimer();
    }
  }

  setRefreshToken(token) {
    this.refreshToken = token;
    if (token) {
      localStorage.setItem('refresh_token', token);
    } else {
      localStorage.removeItem('refresh_token');
    }
  }

  getToken() {
    // IMMER aus localStorage lesen um Synchronisationsprobleme zu vermeiden
    // (z.B. wenn Token nach Login gesetzt wird während andere Requests laufen)
    const token = localStorage.getItem('auth_token');
    this.token = token; // Cache aktualisieren
    return token;
  }

  setCsrfToken(token) {
    this.csrfToken = token;
    if (token) {
      sessionStorage.setItem('csrf_token', token);
    } else {
      sessionStorage.removeItem('csrf_token');
    }
  }

  getCsrfToken() {
    // IMMER aus sessionStorage lesen um Synchronisationsprobleme zu vermeiden
    const token = sessionStorage.getItem('csrf_token');
    this.csrfToken = token; // Cache aktualisieren
    return token;
  }

  // Refresh Access Token using Refresh Token
  async refreshAccessToken() {
    if (this.refreshingToken) {
      // Warte auf laufenden Refresh
      return new Promise((resolve) => {
        const checkRefresh = () => {
          if (!this.refreshingToken) {
            resolve();
          } else {
            setTimeout(checkRefresh, 100);
          }
        };
        checkRefresh();
      });
    }

    this.refreshingToken = true;
    try {
      const refreshToken = localStorage.getItem('refresh_token');
      if (!refreshToken) {
        throw new Error('Kein Refresh-Token vorhanden');
      }

      console.log('[API] Refreshing access token...');
      const response = await fetch('/api/auth/refresh', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ refreshToken })
      });

      if (!response.ok) {
        throw new Error(`Refresh failed: ${response.status}`);
      }

      const data = await response.json();
      this.setToken(data.token);
      if (data.csrfToken) {
        this.setCsrfToken(data.csrfToken);
      }

      console.log('[API] Token refresh successful');
      window.dispatchEvent(new CustomEvent('auth:token-refreshed', {
        detail: { token: data.token }
      }));

    } catch (error) {
      console.log('[API] Token refresh error:', error.message);
      throw error;
    } finally {
      this.refreshingToken = false;
    }
  }

  // Handle authentication failure
  // requestToken: Der Token, der bei der fehlgeschlagenen Anfrage verwendet wurde
  handleAuthFailure(requestToken = null) {
    // Race-Condition Check: Wenn ein neuer Token existiert, der NACH dem
    // fehlgeschlagenen Request gesetzt wurde, nicht ausloggen
    const currentToken = localStorage.getItem('auth_token');

    if (requestToken && currentToken && requestToken !== currentToken) {
      console.log('[API] 401 ignored - new token exists (login occurred after request)');
      return false; // Nicht ausloggen
    }

    console.log('[API] Authentication failed - clearing tokens');
    this.setToken(null);
    this.setRefreshToken(null);
    this.setCsrfToken(null);
    window.dispatchEvent(new CustomEvent('auth:logout'));
    return true;
  }

  // Proaktiver Token-Refresh Timer
  startTokenRefreshTimer() {
    this.clearTokenRefreshTimer();
    // Refresh nach 10 Minuten (Token läuft nach 15 Minuten ab)
    this.refreshTimer = setTimeout(async () => {
      if (this.refreshToken && !this.refreshingToken) {
        try {
          console.log('[API] Proactive token refresh...');
          await this.refreshAccessToken();
        } catch (error) {
          console.log('[API] Proactive refresh failed:', error.message);
          // Bei Fehler nicht automatisch ausloggen, warten bis Token wirklich abläuft
        }
      }
    }, 10 * 60 * 1000); // 10 Minuten
  }

  clearTokenRefreshTimer() {
    if (this.refreshTimer) {
      clearTimeout(this.refreshTimer);
      this.refreshTimer = null;
    }
  }

  // Base Request Method
  async request(endpoint, options = {}) {
    const url = `${this.baseUrl}${endpoint}`;

    const headers = {
      'Content-Type': 'application/json',
      ...options.headers
    };

    // Sicherstellen, dass Token aktuell ist
    if (!this.token && localStorage.getItem('auth_token')) {
      this.init();
    }

    // Add auth token
    const token = this.getToken();
    console.log('[API] Request:', endpoint, 'Token:', token ? token.substring(0, 20) + '...' : 'NULL');
    if (token) {
      headers['Authorization'] = `Bearer ${token}`;
    }

    // Add CSRF token for mutating requests
    if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(options.method)) {
      const csrfToken = this.getCsrfToken();
      if (csrfToken) {
        headers['X-CSRF-Token'] = csrfToken;
      }
    }

    const config = {
      method: options.method || 'GET',
      headers,
      credentials: 'same-origin'
    };

    if (options.body && config.method !== 'GET') {
      config.body = JSON.stringify(options.body);
    }

    try {
      const response = await fetch(url, config);

      // Update CSRF token if provided
      const newCsrfToken = response.headers.get('X-CSRF-Token');
      if (newCsrfToken) {
        this.setCsrfToken(newCsrfToken);
      }

      // Update auth token if refreshed by server
      const newAuthToken = response.headers.get('X-New-Token');
      if (newAuthToken) {
        this.setToken(newAuthToken);
        window.dispatchEvent(new CustomEvent('auth:token-refreshed', {
          detail: { token: newAuthToken }
        }));
      }

      // Handle 401 Unauthorized
      if (response.status === 401) {
        const currentTokenNow = localStorage.getItem('auth_token');
        console.log('[API] 401 received for:', endpoint);
        console.log('[API] Token used in request:', token ? token.substring(0, 20) + '...' : 'NULL');
        console.log('[API] Current token in storage:', currentTokenNow ? currentTokenNow.substring(0, 20) + '...' : 'NULL');
        console.log('[API] Tokens match:', token === currentTokenNow);

        // Versuche Token mit Refresh-Token zu erneuern
        if (this.refreshToken && !this.refreshingToken && !options._tokenRefreshAttempted) {
          console.log('[API] Attempting token refresh...');
          try {
            await this.refreshAccessToken();
            // Wiederhole original Request mit neuem Token
            return this.request(endpoint, { ...options, _tokenRefreshAttempted: true });
          } catch (refreshError) {
            console.log('[API] Token refresh failed:', refreshError.message);
            // Fallback zum Logout - aber nur wenn kein neuer Login stattfand
            if (this.handleAuthFailure(token)) {
              throw new ApiError('Sitzung abgelaufen', 401);
            }
            // Neuer Token existiert - Request mit neuem Token wiederholen (max 1x)
            if (!options._newTokenRetried) {
              return this.request(endpoint, { ...options, _tokenRefreshAttempted: true, _newTokenRetried: true });
            }
            throw new ApiError('Sitzung abgelaufen', 401);
          }
        }

        // Kein Refresh-Token oder Refresh bereits versucht
        // Nur ausloggen wenn kein neuer Token existiert
        if (this.handleAuthFailure(token)) {
          throw new ApiError('Sitzung abgelaufen', 401);
        }
        // Neuer Token existiert - Request mit neuem Token wiederholen (max 1x)
        if (!options._newTokenRetried) {
          return this.request(endpoint, { ...options, _newTokenRetried: true });
        }
        throw new ApiError('Sitzung abgelaufen', 401);
      }

      // Handle CSRF errors - update token and retry once
      if (response.status === 403) {
        const errorData = await response.json().catch(() => ({}));
        if (errorData.code === 'CSRF_ERROR' && errorData.csrfToken) {
          // Store the new CSRF token
          this.setCsrfToken(errorData.csrfToken);

          // Retry the request once with the new token
          if (!options._csrfRetried) {
            return this.request(endpoint, { ...options, _csrfRetried: true });
          }
        }
        throw new ApiError(
          errorData.error || 'Zugriff verweigert',
          response.status,
          errorData
        );
      }

      // Handle other errors
      if (!response.ok) {
        const errorData = await response.json().catch(() => ({}));
        throw new ApiError(
          errorData.error || `HTTP Error ${response.status}`,
          response.status,
          errorData
        );
      }

      // Parse response
      const contentType = response.headers.get('content-type');
      if (contentType && contentType.includes('application/json')) {
        return await response.json();
      }

      return response;
    } catch (error) {
      if (error instanceof ApiError) {
        throw error;
      }

      // Network error
      if (!navigator.onLine) {
        throw new ApiError('Keine Internetverbindung', 0, { offline: true });
      }

      throw new ApiError('Netzwerkfehler: ' + error.message, 0);
    }
  }

  // Convenience Methods
  get(endpoint, options = {}) {
    return this.request(endpoint, { ...options, method: 'GET' });
  }

  post(endpoint, body, options = {}) {
    return this.request(endpoint, { ...options, method: 'POST', body });
  }

  put(endpoint, body, options = {}) {
    return this.request(endpoint, { ...options, method: 'PUT', body });
  }

  patch(endpoint, body, options = {}) {
    return this.request(endpoint, { ...options, method: 'PATCH', body });
  }

  delete(endpoint, options = {}) {
    return this.request(endpoint, { ...options, method: 'DELETE' });
  }

  // File Upload
  async uploadFile(endpoint, file, onProgress, _retried = false) {
    const url = `${this.baseUrl}${endpoint}`;
    const formData = new FormData();
    formData.append('files', file);

    const token = this.getToken();
    const csrfToken = this.getCsrfToken();

    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();

      xhr.open('POST', url);

      if (token) {
        xhr.setRequestHeader('Authorization', `Bearer ${token}`);
      }

      if (csrfToken) {
        xhr.setRequestHeader('X-CSRF-Token', csrfToken);
      }

      xhr.upload.addEventListener('progress', (e) => {
        if (e.lengthComputable && onProgress) {
          const percentage = Math.round((e.loaded / e.total) * 100);
          onProgress(percentage);
        }
      });

      xhr.addEventListener('load', async () => {
        if (xhr.status >= 200 && xhr.status < 300) {
          try {
            resolve(JSON.parse(xhr.responseText));
          } catch {
            resolve(xhr.responseText);
          }
        } else if (xhr.status === 403 && !_retried) {
          // Handle CSRF error - get new token and retry
          try {
            const errorData = JSON.parse(xhr.responseText);
            if (errorData.code === 'CSRF_ERROR' && errorData.csrfToken) {
              this.setCsrfToken(errorData.csrfToken);
              // Retry with new token
              try {
                const result = await this.uploadFile(endpoint, file, onProgress, true);
                resolve(result);
              } catch (retryError) {
                reject(retryError);
              }
              return;
            }
          } catch {}
          reject(new ApiError('Upload fehlgeschlagen', xhr.status));
        } else {
          try {
            const error = JSON.parse(xhr.responseText);
            reject(new ApiError(error.error || 'Upload fehlgeschlagen', xhr.status));
          } catch {
            reject(new ApiError('Upload fehlgeschlagen', xhr.status));
          }
        }
      });

      xhr.addEventListener('error', () => {
        reject(new ApiError('Netzwerkfehler beim Upload', 0));
      });

      xhr.send(formData);
    });
  }

  // Download File
  async downloadFile(endpoint, filename) {
    const url = `${this.baseUrl}${endpoint}`;
    const token = this.getToken();

    const response = await fetch(url, {
      headers: token ? { 'Authorization': `Bearer ${token}` } : {}
    });

    if (!response.ok) {
      throw new ApiError('Download fehlgeschlagen', response.status);
    }

    const blob = await response.blob();
    const downloadUrl = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = downloadUrl;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    window.URL.revokeObjectURL(downloadUrl);
  }

  // =====================
  // AUTH ENDPOINTS
  // =====================

  async login(username, password) {
    console.log('[API] login() called');
    const response = await this.post('/auth/login', { username, password });
    console.log('[API] login() response:', response ? 'OK' : 'NULL', 'token:', response?.token ? 'EXISTS' : 'MISSING');
    this.setToken(response.token);
    
    // Store refresh token if provided (new auth system)
    if (response.refreshToken) {
      this.setRefreshToken(response.refreshToken);
    }
    
    // Store CSRF token from login response
    if (response.csrfToken) {
      this.setCsrfToken(response.csrfToken);
    }
    return response;
  }

  async logout() {
    try {
      await this.post('/auth/logout', {});
    } finally {
      this.setToken(null);
      this.setRefreshToken(null);
      this.setCsrfToken(null);
    }
  }

  async changePassword(currentPassword, newPassword) {
    return this.post('/auth/password', { currentPassword, newPassword });
  }

  async updateUserColor(color) {
    return this.put('/auth/color', { color });
  }

  async getUsers() {
    return this.get('/auth/users');
  }

  // =====================
  // PROJECT ENDPOINTS
  // =====================

  async getProjects() {
    return this.get('/projects');
  }

  async getProject(id) {
    return this.get(`/projects/${id}`);
  }

  async createProject(data) {
    return this.post('/projects', data);
  }

  async updateProject(id, data) {
    return this.put(`/projects/${id}`, data);
  }

  async deleteProject(id, force = true) {
    return this.delete(`/projects/${id}?force=${force}`);
  }

  // =====================
  // COLUMN ENDPOINTS
  // =====================

  async getColumns(projectId) {
    return this.get(`/columns/${projectId}`);
  }

  async createColumn(projectId, data) {
    return this.post('/columns', { ...data, projectId });
  }

  async updateColumn(projectId, columnId, data) {
    return this.put(`/columns/${columnId}`, data);
  }

  async deleteColumn(projectId, columnId) {
    return this.delete(`/columns/${columnId}`);
  }

  async reorderColumns(projectId, columnId, newPosition) {
    return this.put(`/columns/${columnId}/position`, { newPosition });
  }

  // =====================
  // TASK ENDPOINTS
  // =====================

  async getTasks(projectId, params = {}) {
    const queryString = new URLSearchParams(params).toString();
    const endpoint = `/tasks/project/${projectId}${queryString ? '?' + queryString : ''}`;
    return this.get(endpoint);
  }

  async getTask(projectId, taskId) {
    return this.get(`/tasks/${taskId}`);
  }

  async createTask(projectId, data) {
    return this.post('/tasks', { ...data, projectId });
  }

  async updateTask(projectId, taskId, data) {
    return this.put(`/tasks/${taskId}`, data);
  }

  async deleteTask(projectId, taskId) {
    return this.delete(`/tasks/${taskId}`);
  }

  async moveTask(projectId, taskId, columnId, position) {
    return this.put(`/tasks/${taskId}/move`, { columnId, position });
  }

  async duplicateTask(projectId, taskId) {
    return this.post(`/tasks/${taskId}/duplicate`, {});
  }

  async archiveTask(projectId, taskId) {
    return this.put(`/tasks/${taskId}/archive`, { archived: true });
  }

  async restoreTask(projectId, taskId, columnId = null) {
    const data = { archived: false };
    if (columnId) data.columnId = columnId;
    return this.put(`/tasks/${taskId}/archive`, data);
  }

  async getTaskHistory(projectId, taskId) {
    return this.get(`/tasks/${taskId}/history`);
  }

  async searchTasks(projectId, query) {
    return this.get(`/tasks/search?projectId=${projectId}&q=${encodeURIComponent(query)}`);
  }

  // =====================
  // SUBTASK ENDPOINTS
  // =====================

  async getSubtasks(projectId, taskId) {
    return this.get(`/subtasks/${taskId}`);
  }

  async createSubtask(projectId, taskId, data) {
    return this.post('/subtasks', { ...data, taskId });
  }

  async updateSubtask(projectId, taskId, subtaskId, data) {
    return this.put(`/subtasks/${subtaskId}`, data);
  }

  async deleteSubtask(projectId, taskId, subtaskId) {
    return this.delete(`/subtasks/${subtaskId}`);
  }

  async reorderSubtasks(projectId, taskId, subtaskId, newPosition) {
    return this.put(`/subtasks/${subtaskId}/position`, { newPosition });
  }

  // =====================
  // COMMENT ENDPOINTS
  // =====================

  async getComments(projectId, taskId) {
    return this.get(`/comments/${taskId}`);
  }

  async createComment(projectId, taskId, data) {
    return this.post('/comments', { ...data, taskId });
  }

  async updateComment(projectId, taskId, commentId, data) {
    return this.put(`/comments/${commentId}`, data);
  }

  async deleteComment(projectId, taskId, commentId) {
    return this.delete(`/comments/${commentId}`);
  }

  // =====================
  // LABEL ENDPOINTS
  // =====================

  async getLabels(projectId) {
    return this.get(`/labels/${projectId}`);
  }

  async createLabel(projectId, data) {
    return this.post('/labels', { ...data, projectId });
  }

  async updateLabel(projectId, labelId, data) {
    return this.put(`/labels/${labelId}`, data);
  }

  async deleteLabel(projectId, labelId) {
    return this.delete(`/labels/${labelId}`);
  }

  // =====================
  // FILE ENDPOINTS
  // =====================

  async getFiles(projectId, taskId) {
    return this.get(`/files/${taskId}`);
  }

  async uploadTaskFile(projectId, taskId, file, onProgress) {
    return this.uploadFile(`/files/${taskId}`, file, onProgress);
  }

  async downloadTaskFile(projectId, taskId, fileId, filename) {
    return this.downloadFile(`/files/download/${fileId}`, filename);
  }

  async deleteFile(projectId, taskId, fileId) {
    return this.delete(`/files/${fileId}`);
  }

  getFilePreviewUrl(projectId, taskId, fileId) {
    const token = this.getToken();
    const url = `${this.baseUrl}/files/preview/${fileId}`;
    // Add token as query parameter for img src authentication
    return token ? `${url}?token=${encodeURIComponent(token)}` : url;
  }

  // =====================
  // REMINDER ENDPOINTS
  // =====================

  async getReminders(projectId) {
    const response = await this.get(`/reminders?project_id=${projectId}`);
    return response.data || response; // Extract data property or fallback to response
  }

  async createReminder(data) {
    return this.post('/reminders', data);
  }

  async updateReminder(reminderId, data) {
    return this.put(`/reminders/${reminderId}`, data);
  }

  async deleteReminder(reminderId) {
    return this.delete(`/reminders/${reminderId}`);
  }

  async getDueReminders() {
    return this.get('/reminders/due/check');
  }

  // =====================
  // LINK ENDPOINTS
  // =====================

  async getLinks(projectId, taskId) {
    return this.get(`/links/${taskId}`);
  }

  async createLink(projectId, taskId, data) {
    return this.post('/links', { ...data, taskId });
  }

  async updateLink(projectId, taskId, linkId, data) {
    return this.put(`/links/${linkId}`, data);
  }

  async deleteLink(projectId, taskId, linkId) {
    return this.delete(`/links/${linkId}`);
  }

  // =====================
  // TEMPLATE ENDPOINTS
  // =====================

  async getTemplates() {
    return this.get('/templates');
  }

  async getTemplate(id) {
    return this.get(`/templates/${id}`);
  }

  async createTemplate(data) {
    return this.post('/templates', data);
  }

  async updateTemplate(id, data) {
    return this.put(`/templates/${id}`, data);
  }

  async deleteTemplate(id) {
    return this.delete(`/templates/${id}`);
  }

  async createTaskFromTemplate(projectId, templateId, columnId) {
    return this.post(`/templates/${templateId}/apply`, { projectId, columnId });
  }

  // =====================
  // STATS ENDPOINTS
  // =====================

  async getStats(projectId) {
    const endpoint = projectId ? `/stats/dashboard?projectId=${projectId}` : '/stats/dashboard';
    return this.get(endpoint);
  }

  async getCompletionStats(projectId, weeks = 8) {
    const endpoint = projectId
      ? `/stats/completed-per-week?projectId=${projectId}&weeks=${weeks}`
      : `/stats/completed-per-week?weeks=${weeks}`;
    return this.get(endpoint);
  }

  async getTimeStats(projectId) {
    return this.get('/stats/time-per-project');
  }

  async getOverdueTasks(projectId) {
    // Placeholder - overdue data comes from dashboard stats
    return [];
  }

  async getDueTodayTasks(projectId) {
    // Placeholder - due today data comes from dashboard stats
    return [];
  }

  // =====================
  // EXPORT/IMPORT ENDPOINTS
  // =====================

  async exportProject(projectId, format = 'json') {
    const response = await this.get(`/export/project/${projectId}?format=${format}`);

    if (format === 'csv') {
      // For CSV, trigger download
      const blob = new Blob([response], { type: 'text/csv' });
      const url = window.URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = `project_${projectId}_export.csv`;
      a.click();
      window.URL.revokeObjectURL(url);
    }

    return response;
  }

  async exportAll(format = 'json') {
    return this.get(`/export/all?format=${format}`);
  }

  async importProject(data) {
    return this.post('/import', data);
  }

  // =====================
  // HEALTH ENDPOINTS
  // =====================

  async healthCheck() {
    return this.get('/health');
  }

  async getSystemInfo() {
    return this.get('/health/info');
  }

  // =====================
  // ADMIN ENDPOINTS
  // =====================

  async getAdminUsers() {
    return this.get('/admin/users');
  }

  async createAdminUser(data) {
    return this.post('/admin/users', data);
  }

  async updateAdminUser(userId, data) {
    return this.put(`/admin/users/${userId}`, data);
  }

  async deleteAdminUser(userId) {
    return this.delete(`/admin/users/${userId}`);
  }

  async getUploadSettings() {
    return this.get('/admin/upload-settings');
  }

  async updateUploadSettings(settings) {
    return this.put('/admin/upload-settings', settings);
  }

  // =====================
  // PROPOSALS (GENEHMIGUNGEN) ENDPOINTS
  // =====================

  async getProposals(sort = 'date', archived = false, projectId = null) {
    let url = `/proposals?sort=${sort}&archived=${archived ? '1' : '0'}`;
    if (projectId) {
      url += `&projectId=${projectId}`;
    }
    return this.get(url);
  }

  async createProposal(data) {
    return this.post('/proposals', data);
  }

  async approveProposal(proposalId, approved) {
    return this.put(`/proposals/${proposalId}/approve`, { approved });
  }

  async archiveProposal(proposalId, archived) {
    return this.put(`/proposals/${proposalId}/archive`, { archived });
  }

  async deleteProposal(proposalId) {
    return this.delete(`/proposals/${proposalId}`);
  }

  // =====================
  // TASKS FOR PROPOSALS
  // =====================

  async getAllTasks() {
    return this.get('/tasks/all');
  }

  // =====================
  // NOTIFICATION ENDPOINTS
  // =====================

  async getNotifications() {
    return this.get('/notifications');
  }

  async getNotificationCount() {
    return this.get('/notifications/count');
  }

  async markNotificationRead(id) {
    return this.put(`/notifications/${id}/read`, {});
  }

  async markAllNotificationsRead() {
    return this.put('/notifications/read-all', {});
  }

  async deleteNotification(id) {
    return this.delete(`/notifications/${id}`);
  }

  // =====================
  // GITEA ENDPOINTS
  // =====================

  async testGiteaConnection() {
    return this.get('/gitea/test');
  }

  async getGiteaRepositories(page = 1, limit = 50) {
    return this.get(`/gitea/repositories?page=${page}&limit=${limit}`);
  }

  async createGiteaRepository(data) {
    return this.post('/gitea/repositories', data);
  }

  async getGiteaRepository(owner, repo) {
    return this.get(`/gitea/repositories/${owner}/${repo}`);
  }

  async deleteGiteaRepository(owner, repo) {
    return this.delete(`/gitea/repositories/${owner}/${repo}`);
  }

  async getGiteaBranches(owner, repo) {
    return this.get(`/gitea/repositories/${owner}/${repo}/branches`);
  }

  async getGiteaCommits(owner, repo, options = {}) {
    const params = new URLSearchParams();
    if (options.page) params.append('page', options.page);
    if (options.limit) params.append('limit', options.limit);
    if (options.branch) params.append('branch', options.branch);
    const queryString = params.toString();
    return this.get(`/gitea/repositories/${owner}/${repo}/commits${queryString ? '?' + queryString : ''}`);
  }

  async getGiteaUser() {
    return this.get('/gitea/user');
  }

  // =====================
  // APPLICATIONS (Projekt-Repository-Verknüpfung) ENDPOINTS
  // =====================

  async getProjectApplication(projectId) {
    return this.get(`/applications/${projectId}`);
  }

  async saveProjectApplication(data) {
    return this.post('/applications', data);
  }

  async deleteProjectApplication(projectId) {
    return this.delete(`/applications/${projectId}`);
  }

  async getUserBasePath() {
    return this.get('/applications/user/base-path');
  }

  async setUserBasePath(basePath) {
    return this.put('/applications/user/base-path', { basePath });
  }

  async syncProjectApplication(projectId) {
    return this.post(`/applications/${projectId}/sync`, {});
  }

  // =====================
  // GIT OPERATIONS ENDPOINTS
  // =====================

  async cloneRepository(data) {
    return this.post('/git/clone', data);
  }

  async getGitStatus(projectId) {
    return this.get(`/git/status/${projectId}`);
  }

  async gitPull(projectId, branch = null) {
    return this.post(`/git/pull/${projectId}`, { branch });
  }

  async gitPush(projectId, branch = null) {
    return this.post(`/git/push/${projectId}`, { branch });
  }

  async gitCommit(projectId, message, stageAll = true) {
    return this.post(`/git/commit/${projectId}`, { message, stageAll });
  }

  async getGitCommits(projectId, limit = 20) {
    return this.get(`/git/commits/${projectId}?limit=${limit}`);
  }

  async getGitBranches(projectId) {
    return this.get(`/git/branches/${projectId}`);
  }

  async gitCheckout(projectId, branch) {
    return this.post(`/git/checkout/${projectId}`, { branch });
  }

  async gitFetch(projectId) {
    return this.post(`/git/fetch/${projectId}`, {});
  }

  async gitStage(projectId) {
    return this.post(`/git/stage/${projectId}`, {});
  }

  async getGitRemote(projectId) {
    return this.get(`/git/remote/${projectId}`);
  }

  async validatePath(path) {
    return this.post('/git/validate-path', { path });
  }

  async prepareRepository(projectId, repoUrl, branch = 'main') {
    return this.post(`/git/prepare/${projectId}`, { repoUrl, branch });
  }

  async setGitRemote(projectId, repoUrl) {
    return this.post(`/git/set-remote/${projectId}`, { repoUrl });
  }

  async gitInitPush(projectId, targetBranch = null, force = false) {
    return this.post(`/git/init-push/${projectId}`, { targetBranch, force });
  }

  async gitRenameBranch(projectId, oldName, newName) {
    return this.post(`/git/rename-branch/${projectId}`, { oldName, newName });
  }

  // =====================
  // SERVER GIT ENDPOINTS (Server-Modus)
  // =====================

  async getServerGitInfo() {
    return this.get('/git/server/info');
  }

  async getServerGitStatus() {
    return this.get('/git/server/status');
  }

  async getServerGitBranches() {
    return this.get('/git/server/branches');
  }

  async getServerGitCommits(limit = 20) {
    return this.get(`/git/server/commits?limit=${limit}`);
  }

  async getServerGitRemote() {
    return this.get('/git/server/remote');
  }

  async serverGitStage() {
    return this.post('/git/server/stage', {});
  }

  async serverGitCommit(message, stageAll = true) {
    return this.post('/git/server/commit', { message, stageAll });
  }

  async serverGitPush(branch = null, force = false) {
    return this.post('/git/server/push', { branch, force });
  }

  async serverGitPull(branch = null) {
    return this.post('/git/server/pull', { branch });
  }

  async serverGitFetch() {
    return this.post('/git/server/fetch', {});
  }

  async serverGitCheckout(branch) {
    return this.post('/git/server/checkout', { branch });
  }

  // =====================
  // BROWSER-UPLOAD ENDPOINTS
  // =====================

  async prepareBrowserUpload() {
    return this.post('/git/browser-upload-prepare', {});
  }

  async cancelBrowserUpload(sessionId) {
    return this.delete(`/git/browser-upload/${sessionId}`);
  }

  /**
   * Lädt Dateien vom Browser hoch und pusht sie ins Gitea
   * @param {Object} options - Upload-Optionen
   * @param {File[]} options.files - Array von File-Objekten mit relativePath Property
   * @param {string} options.repoUrl - Gitea Repository URL
   * @param {string} options.branch - Ziel-Branch
   * @param {string} options.commitMessage - Commit-Nachricht
   * @param {string} options.sessionId - Session-ID vom prepare-Aufruf
   * @param {Function} options.onProgress - Progress-Callback (optional)
   */
  async browserUploadAndPush(options) {
    const { files, repoUrl, branch, commitMessage, sessionId, onProgress } = options;

    const url = `${this.baseUrl}/git/browser-upload`;
    const formData = new FormData();

    // Metadaten hinzufügen
    formData.append('repoUrl', repoUrl);
    formData.append('branch', branch || 'main');
    formData.append('commitMessage', commitMessage);
    formData.append('sessionId', sessionId);

    // Dateien hinzufügen (mit relativem Pfad als Dateiname)
    files.forEach(fileInfo => {
      // Erstelle neues File-Objekt mit relativem Pfad als Namen
      const file = new File([fileInfo.file], fileInfo.relativePath, {
        type: fileInfo.file.type
      });
      formData.append('files', file);
    });

    const token = this.getToken();
    const csrfToken = this.getCsrfToken();

    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();

      xhr.open('POST', url);

      if (token) {
        xhr.setRequestHeader('Authorization', `Bearer ${token}`);
      }

      if (csrfToken) {
        xhr.setRequestHeader('X-CSRF-Token', csrfToken);
      }

      if (onProgress) {
        xhr.upload.addEventListener('progress', (e) => {
          if (e.lengthComputable) {
            const percentage = Math.round((e.loaded / e.total) * 100);
            onProgress(percentage);
          }
        });
      }

      xhr.addEventListener('load', () => {
        if (xhr.status >= 200 && xhr.status < 300) {
          try {
            resolve(JSON.parse(xhr.responseText));
          } catch {
            resolve({ success: true });
          }
        } else {
          try {
            const error = JSON.parse(xhr.responseText);
            reject(new Error(error.error || 'Upload fehlgeschlagen'));
          } catch {
            reject(new Error('Upload fehlgeschlagen'));
          }
        }
      });

      xhr.addEventListener('error', () => {
        reject(new Error('Netzwerkfehler beim Upload'));
      });

      xhr.send(formData);
    });
  }

  // =====================
  // KNOWLEDGE ENDPOINTS (Wissensmanagement)
  // =====================

  // Kategorien
  async getKnowledgeCategories() {
    return this.get('/knowledge/categories');
  }

  async createKnowledgeCategory(data) {
    return this.post('/knowledge/categories', data);
  }

  async updateKnowledgeCategory(id, data) {
    return this.put(`/knowledge/categories/${id}`, data);
  }

  async deleteKnowledgeCategory(id) {
    return this.delete(`/knowledge/categories/${id}`);
  }

  async updateKnowledgeCategoryPosition(id, newPosition) {
    return this.put(`/knowledge/categories/${id}/position`, { newPosition });
  }

  // Einträge
  async getKnowledgeEntries(categoryId = null) {
    const params = categoryId ? `?categoryId=${categoryId}` : '';
    return this.get(`/knowledge/entries${params}`);
  }

  async getKnowledgeEntry(id) {
    return this.get(`/knowledge/entries/${id}`);
  }

  async createKnowledgeEntry(data) {
    return this.post('/knowledge/entries', data);
  }

  async updateKnowledgeEntry(id, data) {
    return this.put(`/knowledge/entries/${id}`, data);
  }

  async deleteKnowledgeEntry(id) {
    return this.delete(`/knowledge/entries/${id}`);
  }

  async updateKnowledgeEntryPosition(id, newPosition, newCategoryId = null) {
    return this.put(`/knowledge/entries/${id}/position`, { newPosition, newCategoryId });
  }

  // Anhänge
  async getKnowledgeAttachments(entryId) {
    return this.get(`/knowledge/attachments/${entryId}`);
  }

  async uploadKnowledgeAttachment(entryId, file, onProgress) {
    return this.uploadFile(`/knowledge/attachments/${entryId}`, file, onProgress);
  }

  async deleteKnowledgeAttachment(id) {
    return this.delete(`/knowledge/attachments/${id}`);
  }

  getKnowledgeAttachmentDownloadUrl(id) {
    return `${this.baseUrl}/knowledge/attachments/download/${id}`;
  }

  // Suche
  async searchKnowledge(query) {
    return this.get(`/knowledge/search?q=${encodeURIComponent(query)}`);
  }

  // =====================
  // CODING
  // =====================

  async getCodingDirectories() {
    return this.get('/coding/directories');
  }

  async createCodingDirectory(data) {
    return this.post('/coding/directories', data);
  }

  async updateCodingDirectory(id, data) {
    return this.put(`/coding/directories/${id}`, data);
  }

  async deleteCodingDirectory(id) {
    return this.delete(`/coding/directories/${id}`);
  }

  async getCodingDirectoryStatus(id) {
    return this.get(`/coding/directories/${id}/status`);
  }

  async codingGitFetch(id) {
    return this.post(`/coding/directories/${id}/fetch`);
  }

  async codingGitPull(id) {
    return this.post(`/coding/directories/${id}/pull`);
  }

  async codingGitPush(id, force = false) {
    return this.post(`/coding/directories/${id}/push`, { force });
  }

  async codingGitCommit(id, message) {
    return this.post(`/coding/directories/${id}/commit`, { message });
  }

  async getCodingDirectoryBranches(id) {
    return this.get(`/coding/directories/${id}/branches`);
  }

  async codingGitCheckout(id, branch) {
    return this.post(`/coding/directories/${id}/checkout`, { branch });
  }

  async getCodingDirectoryCommits(id, limit = 20) {
    return this.get(`/coding/directories/${id}/commits?limit=${limit}`);
  }

  async validateCodingPath(path) {
    return this.post('/coding/validate-path', { path });
  }

  async getCodingDirectoryUsage(id) {
    return this.get(`/coding/directories/${id}/usage`);
  }

  async getCodingDirectoryUsageHistory(id, hours = 24) {
    return this.get(`/coding/directories/${id}/usage/history?hours=${hours}`);
  }

  // =============================================================================
  // CONTACTS
  // =============================================================================

  async getContacts(params = {}) {
    const queryString = new URLSearchParams(params).toString();
    return this.get(`/contacts${queryString ? '?' + queryString : ''}`);
  }

  async getContact(id) {
    return this.get(`/contacts/${id}`);
  }

  async createContact(data) {
    return this.post('/contacts', data);
  }

  async updateContact(id, data) {
    return this.put(`/contacts/${id}`, data);
  }

  async deleteContact(id) {
    return this.delete(`/contacts/${id}`);
  }

  async getContactTags() {
    return this.get('/contacts/tags/all');
  }
}

// Custom API Error Class
class ApiError extends Error {
  constructor(message, status, data = {}) {
    super(message);
    this.name = 'ApiError';
    this.status = status;
    this.data = data;
  }

  get isOffline() {
    return this.data.offline === true;
  }

  get isUnauthorized() {
    return this.status === 401;
  }

  get isNotFound() {
    return this.status === 404;
  }

  get isValidationError() {
    return this.status === 400;
  }

  get isServerError() {
    return this.status >= 500;
  }
}

// Create singleton instance
const api = new ApiClient();

export { api, ApiError };
export default api;
