import { describe, expect, it, vi, afterEach, beforeEach } from "vitest";

// Mock fetchWithSsrFGuard from plugin-sdk
vi.mock("openclaw/plugin-sdk", async (importOriginal) => {
  const actual = await importOriginal<typeof import("openclaw/plugin-sdk")>();
  return {
    ...actual,
    fetchWithSsrFGuard: vi.fn(),
  };
});

// Mock @tloncorp/api
vi.mock("@tloncorp/api", () => ({
  uploadFile: vi.fn(),
}));

describe("uploadImageFromUrl", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  it("fetches image and calls uploadFile, returns uploaded URL", async () => {
    const { fetchWithSsrFGuard } = await import("openclaw/plugin-sdk");
    const mockFetch = vi.mocked(fetchWithSsrFGuard);

    const { uploadFile } = await import("@tloncorp/api");
    const mockUploadFile = vi.mocked(uploadFile);

    // Mock fetchWithSsrFGuard to return a successful response with a blob
    const mockBlob = new Blob(["fake-image"], { type: "image/png" });
    mockFetch.mockResolvedValue({
      response: {
        ok: true,
        headers: new Headers({ "content-type": "image/png" }),
        blob: () => Promise.resolve(mockBlob),
      } as unknown as Response,
      finalUrl: "https://example.com/image.png",
      release: vi.fn().mockResolvedValue(undefined),
    });

    // Mock uploadFile to return a successful upload
    mockUploadFile.mockResolvedValue({ url: "https://memex.tlon.network/uploaded.png" });

    const { uploadImageFromUrl } = await import("./upload.js");
    const result = await uploadImageFromUrl("https://example.com/image.png");

    expect(result).toBe("https://memex.tlon.network/uploaded.png");
    expect(mockUploadFile).toHaveBeenCalledTimes(1);
    expect(mockUploadFile).toHaveBeenCalledWith(
      expect.objectContaining({
        blob: mockBlob,
        contentType: "image/png",
      }),
    );
  });

  it("returns original URL if fetch fails", async () => {
    const { fetchWithSsrFGuard } = await import("openclaw/plugin-sdk");
    const mockFetch = vi.mocked(fetchWithSsrFGuard);

    // Mock fetchWithSsrFGuard to return a failed response
    mockFetch.mockResolvedValue({
      response: {
        ok: false,
        status: 404,
      } as unknown as Response,
      finalUrl: "https://example.com/image.png",
      release: vi.fn().mockResolvedValue(undefined),
    });

    const { uploadImageFromUrl } = await import("./upload.js");
    const result = await uploadImageFromUrl("https://example.com/image.png");

    expect(result).toBe("https://example.com/image.png");
  });

  it("returns original URL if upload fails", async () => {
    const { fetchWithSsrFGuard } = await import("openclaw/plugin-sdk");
    const mockFetch = vi.mocked(fetchWithSsrFGuard);

    const { uploadFile } = await import("@tloncorp/api");
    const mockUploadFile = vi.mocked(uploadFile);

    // Mock fetchWithSsrFGuard to return a successful response
    const mockBlob = new Blob(["fake-image"], { type: "image/png" });
    mockFetch.mockResolvedValue({
      response: {
        ok: true,
        headers: new Headers({ "content-type": "image/png" }),
        blob: () => Promise.resolve(mockBlob),
      } as unknown as Response,
      finalUrl: "https://example.com/image.png",
      release: vi.fn().mockResolvedValue(undefined),
    });

    // Mock uploadFile to throw an error
    mockUploadFile.mockRejectedValue(new Error("Upload failed"));

    const { uploadImageFromUrl } = await import("./upload.js");
    const result = await uploadImageFromUrl("https://example.com/image.png");

    expect(result).toBe("https://example.com/image.png");
  });

  it("rejects non-http(s) URLs", async () => {
    const { uploadImageFromUrl } = await import("./upload.js");

    // file:// URL should be rejected
    const result = await uploadImageFromUrl("file:///etc/passwd");
    expect(result).toBe("file:///etc/passwd");

    // ftp:// URL should be rejected
    const result2 = await uploadImageFromUrl("ftp://example.com/image.png");
    expect(result2).toBe("ftp://example.com/image.png");
  });

  it("handles invalid URLs gracefully", async () => {
    const { uploadImageFromUrl } = await import("./upload.js");

    // Invalid URL should return original
    const result = await uploadImageFromUrl("not-a-valid-url");
    expect(result).toBe("not-a-valid-url");
  });

  it("extracts filename from URL path", async () => {
    const { fetchWithSsrFGuard } = await import("openclaw/plugin-sdk");
    const mockFetch = vi.mocked(fetchWithSsrFGuard);

    const { uploadFile } = await import("@tloncorp/api");
    const mockUploadFile = vi.mocked(uploadFile);

    const mockBlob = new Blob(["fake-image"], { type: "image/jpeg" });
    mockFetch.mockResolvedValue({
      response: {
        ok: true,
        headers: new Headers({ "content-type": "image/jpeg" }),
        blob: () => Promise.resolve(mockBlob),
      } as unknown as Response,
      finalUrl: "https://example.com/path/to/my-image.jpg",
      release: vi.fn().mockResolvedValue(undefined),
    });

    mockUploadFile.mockResolvedValue({ url: "https://memex.tlon.network/uploaded.jpg" });

    const { uploadImageFromUrl } = await import("./upload.js");
    await uploadImageFromUrl("https://example.com/path/to/my-image.jpg");

    expect(mockUploadFile).toHaveBeenCalledWith(
      expect.objectContaining({
        fileName: "my-image.jpg",
      }),
    );
  });

  it("uses default filename when URL has no path", async () => {
    const { fetchWithSsrFGuard } = await import("openclaw/plugin-sdk");
    const mockFetch = vi.mocked(fetchWithSsrFGuard);

    const { uploadFile } = await import("@tloncorp/api");
    const mockUploadFile = vi.mocked(uploadFile);

    const mockBlob = new Blob(["fake-image"], { type: "image/png" });
    mockFetch.mockResolvedValue({
      response: {
        ok: true,
        headers: new Headers({ "content-type": "image/png" }),
        blob: () => Promise.resolve(mockBlob),
      } as unknown as Response,
      finalUrl: "https://example.com/",
      release: vi.fn().mockResolvedValue(undefined),
    });

    mockUploadFile.mockResolvedValue({ url: "https://memex.tlon.network/uploaded.png" });

    const { uploadImageFromUrl } = await import("./upload.js");
    await uploadImageFromUrl("https://example.com/");

    expect(mockUploadFile).toHaveBeenCalledWith(
      expect.objectContaining({
        fileName: expect.stringMatching(/^upload-\d+\.png$/),
      }),
    );
  });
});
