// src/kohaku-hub-ui/src/utils/api.js
import axios from "axios";

const api = axios.create({
  timeout: 30000,
  withCredentials: true,
});

// Request interceptor
api.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem("hf_token");
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  },
);

// Response interceptor
api.interceptors.response.use(
  (response) => response,
  (error) => {
    // Don't auto-redirect on 401, let components handle it
    // This allows visitors to browse public content without login
    return Promise.reject(error);
  },
);

export default api;

/**
 * Auth API
 */
export const authAPI = {
  register: (data) => api.post("/api/auth/register", data),
  login: (data) => api.post("/api/auth/login", data),
  logout: () => api.post("/api/auth/logout"),
  me: () => api.get("/api/auth/me"),
  createToken: (data) => api.post("/api/auth/tokens/create", data),
  listTokens: () => api.get("/api/auth/tokens"),
  revokeToken: (id) => api.delete(`/api/auth/tokens/${id}`),
};

/**
 * Repo API
 * Backend expects:
 * - create: { type, name, organization, private, sdk? }
 * - delete: { type, name, organization?, sdk? }
 */
export const repoAPI = {
  /**
   * Create a new repository
   * @param {Object} data - { type: string, name: string, organization: string|null, private: boolean }
   * @returns {Promise} - { url: string, repo_id: string }
   */
  create: (data) => api.post("/api/repos/create", data),

  /**
   * Delete a repository
   * @param {Object} data - { type: string, name: string, organization?: string }
   * @returns {Promise} - { message: string }
   */
  delete: (data) => api.delete("/api/repos/delete", { data }),

  /**
   * Get repository info
   * @param {string} type - Repository type (model/dataset/space)
   * @param {string} namespace - Owner namespace
   * @param {string} name - Repository name
   * @returns {Promise} - Repository metadata
   */
  getInfo: (type, namespace, name) =>
    api.get(`/api/${type}s/${namespace}/${name}`),

  /**
   * List repositories
   * @param {string} type - Repository type (model/dataset/space)
   * @param {Object} params - Query parameters { author?, limit? }
   * @returns {Promise} - Array of repositories
   */
  listRepos: (type, params) => api.get(`/api/${type}s`, { params }),

  /**
   * Get user overview with all repositories
   * @param {string} username - Username
   * @param {number} limit - Max repos per type
   * @returns {Promise} - { models: [], datasets: [], spaces: [] }
   */
  getUserOverview: (username, limit = 100) =>
    api.get(`/api/users/${username}/repos`, { params: { limit } }),

  /**
   * List repository file tree
   * @param {string} type - Repository type
   * @param {string} namespace - Owner namespace
   * @param {string} name - Repository name
   * @param {string} revision - Branch name or commit hash
   * @param {string} path - Path within repository (with leading /)
   * @param {Object} params - Query parameters { recursive?, expand? }
   * @returns {Promise} - Array of files and directories
   */
  listTree: (type, namespace, name, revision, path, params) =>
    api.get(`/api/${type}s/${namespace}/${name}/tree/${revision}${path}`, {
      params,
    }),

  /**
   * Upload files to repository
   * @param {string} type - Repository type
   * @param {string} namespace - Owner namespace
   * @param {string} name - Repository name
   * @param {string} revision - Branch name
   * @param {Object} data - { files: Array<{path, content}>, message: string, description?: string }
   * @param {Function} onProgress - Progress callback
   * @returns {Promise} - Commit result
   */
  uploadFiles: async (type, namespace, name, revision, data, onProgress) => {
    // Convert files to NDJSON format for commit API
    const ndjsonLines = [];

    // Header
    ndjsonLines.push({
      key: "header",
      value: {
        summary: data.message,
        description: data.description || "",
      },
    });

    // Files
    for (const file of data.files) {
      const reader = new FileReader();
      const content = await new Promise((resolve, reject) => {
        reader.onload = (e) => resolve(e.target.result);
        reader.onerror = reject;
        reader.readAsDataURL(file.file);
      });

      // Extract base64 content (remove data:...;base64, prefix)
      const base64Content = content.split(",")[1];

      ndjsonLines.push({
        key: "file",
        value: {
          path: file.path,
          content: base64Content,
          encoding: "base64",
        },
      });
    }

    // Convert to NDJSON
    const ndjson = ndjsonLines.map((line) => JSON.stringify(line)).join("\n");

    return api.post(
      `/api/${type}s/${namespace}/${name}/commit/${revision}`,
      ndjson,
      {
        headers: {
          "Content-Type": "application/x-ndjson",
        },
        onUploadProgress: (progressEvent) => {
          if (onProgress && progressEvent.total) {
            const progress = progressEvent.loaded / progressEvent.total;
            onProgress(progress);
          }
        },
      },
    );
  },

  /**
   * Commit file changes to repository
   * @param {string} type - Repository type
   * @param {string} namespace - Owner namespace
   * @param {string} name - Repository name
   * @param {string} revision - Branch name
   * @param {Object} data - { files: Array<{path, content}>, message: string, description?: string }
   * @returns {Promise} - Commit result
   */
  commitFiles: async (type, namespace, name, revision, data) => {
    // Convert files to NDJSON format for commit API
    const ndjsonLines = [];

    // Header
    ndjsonLines.push({
      key: "header",
      value: {
        summary: data.message,
        description: data.description || "",
      },
    });

    // Files
    for (const file of data.files) {
      // Encode content as base64
      const base64Content = btoa(unescape(encodeURIComponent(file.content)));

      ndjsonLines.push({
        key: "file",
        value: {
          path: file.path,
          content: base64Content,
          encoding: "base64",
        },
      });
    }

    // Convert to NDJSON
    const ndjson = ndjsonLines.map((line) => JSON.stringify(line)).join("\n");

    return api.post(
      `/api/${type}s/${namespace}/${name}/commit/${revision}`,
      ndjson,
      {
        headers: {
          "Content-Type": "application/x-ndjson",
        },
      },
    );
  },

  /**
   * List commits for a repository branch
   * @param {string} type - Repository type
   * @param {string} namespace - Repository namespace
   * @param {string} name - Repository name
   * @param {string} branch - Branch name (default: main)
   * @param {Object} params - Query parameters { limit?, after? }
   * @returns {Promise} - { commits: Array, hasMore: boolean, nextCursor: string }
   */
  listCommits: (type, namespace, name, branch, params) =>
    api.get(`/api/${type}s/${namespace}/${name}/commits/${branch}`, { params }),
};

/**
 * Organization API
 */
export const orgAPI = {
  /**
   * Create a new organization
   * @param {Object} data - { name: string, description?: string }
   * @returns {Promise} - { success: boolean, name: string }
   */
  create: (data) => api.post("/org/create", data),

  /**
   * Get organization details
   * @param {string} name - Organization name
   * @returns {Promise} - { name, description, created_at }
   */
  get: (name) => api.get(`/org/${name}`),

  /**
   * Add member to organization
   * @param {string} name - Organization name
   * @param {Object} data - { username: string, role: string }
   * @returns {Promise} - { success: boolean, message: string }
   */
  addMember: (name, data) => api.post(`/org/${name}/members`, data),

  /**
   * Remove member from organization
   * @param {string} name - Organization name
   * @param {string} username - Username to remove
   * @returns {Promise} - { success: boolean, message: string }
   */
  removeMember: (name, username) =>
    api.delete(`/org/${name}/members/${username}`),

  /**
   * Update member role
   * @param {string} name - Organization name
   * @param {string} username - Username to update
   * @param {Object} data - { role: string }
   * @returns {Promise} - { success: boolean, message: string }
   */
  updateMemberRole: (name, username, data) =>
    api.put(`/org/${name}/members/${username}`, data),

  /**
   * Get user's organizations
   * @param {string} username - Username
   * @returns {Promise} - { organizations: Array<{ name, description, role }> }
   */
  getUserOrgs: (username) => api.get(`/org/users/${username}/orgs`),

  /**
   * Update organization settings
   * @param {string} orgName - Organization name
   * @param {Object} data - { description?: string }
   * @returns {Promise} - { success: boolean, message: string }
   */
  updateSettings: (orgName, data) =>
    api.put(`/api/organizations/${orgName}/settings`, data),

  /**
   * List organization members
   * @param {string} orgName - Organization name
   * @returns {Promise} - { members: Array<{ user: string, role: string }> }
   */
  listMembers: (orgName) => api.get(`/org/${orgName}/members`),
};

/**
 * Settings API
 */
export const settingsAPI = {
  /**
   * Get current user info with organizations (HuggingFace compatible)
   * @returns {Promise} - { type, name, fullname, email, emailVerified, orgs }
   */
  whoamiV2: () => api.get("/api/whoami-v2"),

  /**
   * Update user settings
   * @param {string} username - Username
   * @param {Object} data - { email?: string, fullname?: string }
   * @returns {Promise} - { success: boolean, message: string }
   */
  updateUserSettings: (username, data) =>
    api.put(`/api/users/${username}/settings`, data),

  /**
   * Update repository settings
   * @param {string} repoType - Repository type (model/dataset/space)
   * @param {string} namespace - Repository namespace
   * @param {string} name - Repository name
   * @param {Object} data - { private?: boolean, gated?: string }
   * @returns {Promise} - { success: boolean, message: string }
   */
  updateRepoSettings: (repoType, namespace, name, data) =>
    api.put(`/api/${repoType}s/${namespace}/${name}/settings`, data),

  /**
   * Move/rename repository
   * @param {Object} data - { fromRepo: string, toRepo: string, type: string }
   * @returns {Promise} - { success: boolean, url: string, message: string }
   */
  moveRepo: (data) => api.post("/api/repos/move", data),

  /**
   * Create branch
   * @param {string} repoType - Repository type
   * @param {string} namespace - Repository namespace
   * @param {string} name - Repository name
   * @param {Object} data - { branch: string, revision?: string }
   * @returns {Promise} - { success: boolean, message: string }
   */
  createBranch: (repoType, namespace, name, data) =>
    api.post(`/api/${repoType}s/${namespace}/${name}/branch`, data),

  /**
   * Delete branch
   * @param {string} repoType - Repository type
   * @param {string} namespace - Repository namespace
   * @param {string} name - Repository name
   * @param {string} branch - Branch name
   * @returns {Promise} - { success: boolean, message: string }
   */
  deleteBranch: (repoType, namespace, name, branch) =>
    api.delete(`/api/${repoType}s/${namespace}/${name}/branch/${branch}`),

  /**
   * Create tag
   * @param {string} repoType - Repository type
   * @param {string} namespace - Repository namespace
   * @param {string} name - Repository name
   * @param {Object} data - { tag: string, revision?: string, message?: string }
   * @returns {Promise} - { success: boolean, message: string }
   */
  createTag: (repoType, namespace, name, data) =>
    api.post(`/api/${repoType}s/${namespace}/${name}/tag`, data),

  /**
   * Delete tag
   * @param {string} repoType - Repository type
   * @param {string} namespace - Repository namespace
   * @param {string} name - Repository name
   * @param {string} tag - Tag name
   * @returns {Promise} - { success: boolean, message: string }
   */
  deleteTag: (repoType, namespace, name, tag) =>
    api.delete(`/api/${repoType}s/${namespace}/${name}/tag/${tag}`),
};
