import type { TFile, Vault } from "obsidian";
import { requestUrl } from "obsidian";
import { logger } from "../utils/logger";
import { ApiPaths } from "../utils/api-paths";

export default class ImageUploader {
  private readonly siteUrl: string;
  private readonly token: string;
  private readonly headers: Record<string, string>;
  private readonly uploadedImagesCache: Map<string, string>;

  constructor(siteUrl: string, token: string) {
    this.siteUrl = siteUrl;
    this.token = token;
    this.uploadedImagesCache = new Map();

    this.headers = {
      Authorization: `Bearer ${this.token}`,
    };
  }

  private toBase64(arrayBuffer: ArrayBuffer): string {
    const bytes = new Uint8Array(arrayBuffer);
    let binary = "";
    for (let i = 0; i < bytes.byteLength; i++) {
      binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
  }

  public async uploadImage(filePath: string, vault: Vault): Promise<string | null> {
    const normalizedPath = filePath.replace(/\\/g, "/");

    if (this.uploadedImagesCache.has(normalizedPath)) {
      return this.uploadedImagesCache.get(normalizedPath) || null;
    }

    try {
      const file = vault.getAbstractFileByPath(normalizedPath);

      if (!file || !(file instanceof TFile)) {
        logger.error("ImageUploader", `文件不存在或不是有效文件: ${normalizedPath}`);
        return null;
      }

      const arrayBuffer = await vault.readBinary(file);
      const base64Data = this.toBase64(arrayBuffer);
      const filename = normalizedPath.split("/").pop() || "image";

      const boundary = `----ObsidianHalo${Date.now()}`;
      let body = `--${boundary}\r\n`;
      body += `Content-Disposition: form-data; name="file"; filename="${filename}"\r\n`;
      body += "Content-Type: application/octet-stream\r\n";
      body += "Content-Transfer-Encoding: base64\r\n\r\n";
      body += base64Data;
      body += `\r\n--${boundary}--\r\n`;

      const response = await requestUrl({
        url: `${this.siteUrl}${ApiPaths.attachments.upload()}`,
        method: "POST",
        headers: {
          ...this.headers,
          "Content-Type": `multipart/form-data; boundary=${boundary}`,
        },
        body: body,
      });

      const jsonResponse = response.json;

      if (jsonResponse?.url) {
        this.uploadedImagesCache.set(normalizedPath, jsonResponse.url);
        return jsonResponse.url;
      }

      return null;
    } catch (error) {
      logger.error("ImageUploader", `上传图片失败: ${filePath}`, error);
      return null;
    }
  }

  public async uploadImages(filePaths: string[], vault: Vault): Promise<Map<string, string>> {
    const result = new Map<string, string>();

    for (const filePath of filePaths) {
      const url = await this.uploadImage(filePath, vault);
      if (url) {
        result.set(filePath, url);
      }
    }

    return result;
  }

  public getCachedUrl(filePath: string): string | undefined {
    const normalizedPath = filePath.replace(/\\/g, "/");
    return this.uploadedImagesCache.get(normalizedPath);
  }
}
