const API_BASE = process.env.NEXT_PUBLIC_API_URL || '';
import * as tus from 'tus-js-client';

interface RequestOptions {
  method?: string;
  body?: any;
  headers?: Record<string, string>;
}

class ApiClient {
  private accessToken: string | null = null;
  private refreshToken: string | null = null;

  constructor() {
    if (typeof window !== 'undefined') {
      this.accessToken = localStorage.getItem('access_token');
      this.refreshToken = localStorage.getItem('refresh_token');
    }
  }

  setTokens(accessToken: string, refreshToken: string) {
    this.accessToken = accessToken;
    this.refreshToken = refreshToken;
    localStorage.setItem('access_token', accessToken);
    localStorage.setItem('refresh_token', refreshToken);
  }

  clearTokens() {
    this.accessToken = null;
    this.refreshToken = null;
    localStorage.removeItem('access_token');
    localStorage.removeItem('refresh_token');
  }

  getAccessToken() {
    return this.accessToken;
  }

  isAuthenticated() {
    return !!this.accessToken;
  }

  private async request(endpoint: string, options: RequestOptions = {}) {
    const headers: Record<string, string> = {
      ...options.headers,
    };

    if (this.accessToken) {
      headers['Authorization'] = `Bearer ${this.accessToken}`;
    }

    if (options.body && !(options.body instanceof FormData)) {
      headers['Content-Type'] = 'application/json';
    }

    const response = await fetch(`${API_BASE}${endpoint}`, {
      method: options.method || 'GET',
      headers,
      body: options.body instanceof FormData
        ? options.body
        : options.body
          ? JSON.stringify(options.body)
          : undefined,
    });

    // Try to refresh token on 401
    if (response.status === 401 && this.refreshToken && endpoint !== '/api/auth/refresh') {
      const refreshed = await this.refreshAccessToken();
      if (refreshed) {
        headers['Authorization'] = `Bearer ${this.accessToken}`;
        return fetch(`${API_BASE}${endpoint}`, {
          method: options.method || 'GET',
          headers,
          body: options.body instanceof FormData
            ? options.body
            : options.body
              ? JSON.stringify(options.body)
              : undefined,
        });
      }
    }

    return response;
  }

  private async refreshAccessToken(): Promise<boolean> {
    try {
      const response = await fetch(`${API_BASE}/api/auth/refresh`, {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${this.refreshToken}`,
        },
      });

      if (response.ok) {
        const data = await response.json();
        this.accessToken = data.access_token;
        localStorage.setItem('access_token', data.access_token);
        return true;
      }
    } catch {
      // Refresh failed
    }

    this.clearTokens();
    return false;
  }

  // --- Setup ---
  async checkSetup() {
    const res = await this.request('/api/setup/status');
    return res.json();
  }

  async setup(username: string, password: string) {
    const res = await this.request('/api/setup', {
      method: 'POST',
      body: { username, password },
    });
    return res.json();
  }

  // --- Auth ---
  async login(username: string, password: string, totpCode?: string) {
    const res = await this.request('/api/auth/login', {
      method: 'POST',
      body: { username, password, totp_code: totpCode },
    });
    const data = await res.json();
    if (res.ok && data.access_token) {
      this.setTokens(data.access_token, data.refresh_token);
    }
    return { ok: res.ok, status: res.status, data };
  }

  logout() {
    this.clearTokens();
  }

  // --- Files ---
  async listDirectory(path: string = '.') {
    const res = await this.request(`/api/files/?path=${encodeURIComponent(path)}`);
    return res.json();
  }

  async createFolder(path: string) {
    const res = await this.request('/api/files/mkdir', {
      method: 'POST',
      body: { path },
    });
    return res.json();
  }

  async rename(oldPath: string, newName: string) {
    const res = await this.request('/api/files/rename', {
      method: 'POST',
      body: { old_path: oldPath, new_name: newName },
    });
    return res.json();
  }

  async move(sourcePath: string, destPath: string) {
    const res = await this.request('/api/files/move', {
      method: 'POST',
      body: { source_path: sourcePath, dest_path: destPath },
    });
    return res.json();
  }

  async copy(sourcePath: string, destPath: string) {
    const res = await this.request('/api/files/copy', {
      method: 'POST',
      body: { source_path: sourcePath, dest_path: destPath },
    });
    return res.json();
  }

  async deleteFile(path: string) {
    const res = await this.request(`/api/files/${encodeURIComponent(path)}`, {
      method: 'DELETE',
    });
    return res.json();
  }

  upload(file: File, destPath: string, callbacks?: { onProgress?: (percent: number) => void }): Promise<void> {
    return new Promise((resolve, reject) => {
      const upload = new tus.Upload(file, {
        endpoint: `${API_BASE}/api/tus/`,
        chunkSize: 5 * 1024 * 1024, // 5MB chunks bypass Next.js 10MB proxy limit
        retryDelays: [0, 3000, 5000, 10000, 20000],
        headers: this.getStreamHeaders(),
        metadata: {
          filename: file.name,
          filetype: file.type,
          path: destPath,
        },
        onError: function (error) {
          reject(error);
        },
        onProgress: function (bytesUploaded, bytesTotal) {
          const percentage = (bytesUploaded / bytesTotal) * 100;
          callbacks?.onProgress?.(percentage);
        },
        onSuccess: function () {
          resolve();
        },
      });
      upload.start();
    });
  }

  getDownloadUrl(path: string) {
    const url = `${API_BASE}/api/files/download/?path=${encodeURIComponent(path)}`;
    return this.accessToken ? `${url}&token=${this.accessToken}` : url;
  }

  getThumbnailUrl(checksum: string) {
    const url = `${API_BASE}/api/files/thumbnail/${encodeURIComponent(checksum)}`;
    return this.accessToken ? `${url}?token=${this.accessToken}` : url;
  }

  getStreamHeaders(): Record<string, string> {
    return this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {};
  }

  // --- Search ---
  async search(query: string) {
    const res = await this.request(`/api/search?q=${encodeURIComponent(query)}`);
    return res.json();
  }

  // --- Pinned ---
  async listPinned() {
    const res = await this.request('/api/files/pinned');
    return res.json();
  }

  async togglePin(path: string) {
    const res = await this.request('/api/files/pin', {
      method: 'POST',
      body: { path },
    });
    return res.json();
  }

  async getTasks() {
    const res = await this.request('/api/tasks');
    return res.json();
  }

  async getSettings() {
    const res = await this.request('/api/settings');
    return res.json();
  }

  async updateSettings(settings: any) {
    const res = await this.request('/api/settings', {
      method: 'PUT',
      body: settings,
    });
    return res.json();
  }

  async getStorageDashboard() {
    const res = await this.request('/api/storage/dashboard');
    return res.json();
  }

  // --- Trash ---
  async listTrash() {
    const res = await this.request('/api/trash');
    return res.json();
  }

  async restoreFromTrash(path: string) {
    const res = await this.request('/api/trash/restore', {
      method: 'POST',
      body: { path },
    });
    return res.json();
  }

  async emptyTrash() {
    const res = await this.request('/api/trash', { method: 'DELETE' });
    return res.json();
  }



  // --- Audit ---
  async getAuditLog(limit = 100, offset = 0) {
    const res = await this.request(`/api/audit?limit=${limit}&offset=${offset}`);
    return res.json();
  }

  // --- 2FA ---
  async enable2FA() {
    const res = await this.request('/api/auth/2fa/enable', { method: 'POST' });
    return res.json();
  }

  async confirm2FA(code: string) {
    const res = await this.request('/api/auth/2fa/confirm', {
      method: 'POST',
      body: { code },
    });
    return res.json();
  }

  async disable2FA() {
    const res = await this.request('/api/auth/2fa/disable', { method: 'POST' });
    return res.json();
  }

  // --- Sharing ---
  async createShare(path: string, password?: string, expiresIn?: number, maxDownloads?: number) {
    const res = await this.request('/api/share', {
      method: 'POST',
      body: { path, password, expires_in: expiresIn, max_downloads: maxDownloads },
    });
    return res.json();
  }

  async listShares() {
    const res = await this.request('/api/share');
    return res.json();
  }

  async revokeShare(id: string) {
    const res = await this.request(`/api/share/${id}`, { method: 'DELETE' });
    return res.json();
  }

  async getPublicShare(id: string, password?: string) {
    // Note: No auth headers here, so we use fetch directly to avoid interceptors
    const res = await fetch(`${API_BASE}/api/public/share/${id}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ password }),
    });
    if (!res.ok) {
      const err = await res.json().catch(() => ({}));
      throw err;
    }
    return res.json();
  }

  getPublicDownloadUrl(id: string, password?: string) {
    let url = `${API_BASE}/api/public/download/${id}`;
    if (password) {
      url += `?pwd=${encodeURIComponent(password)}`;
    }
    return url;
  }
}

export const api = new ApiClient();
export default api;
