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

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

  // Token Management
  setToken(token) {
    this.token = token;
    if (token) {
      localStorage.setItem('auth_token', token);
    } else {
      localStorage.removeItem('auth_token');
    }
  }

  getToken() {
    if (!this.token) {
      this.token = localStorage.getItem('auth_token');
    }
    return this.token;
  }

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

  getCsrfToken() {
    if (!this.csrfToken) {
      this.csrfToken = sessionStorage.getItem('csrf_token');
    }
    return this.csrfToken;
  }

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

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

    // Add auth token
    const token = this.getToken();
    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);
      }

      // Handle 401 Unauthorized
      if (response.status === 401) {
        this.setToken(null);
        window.dispatchEvent(new CustomEvent('auth:logout'));
        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) {
    const response = await this.post('/auth/login', { username, password });
    this.setToken(response.token);
    // 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.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) {
    return this.put(`/tasks/${taskId}/archive`, { archived: false });
  }

  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;
  }

  // =====================
  // 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, branch = 'main') {
    return this.post(`/git/init-push/${projectId}`, { branch });
  }
}

// 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;
