import { randomUUID } from "node:crypto";
import fs from "node:fs/promises";
import path from "node:path";
import axios from "axios";
import { config } from "../config";
import { logger } from "../utils/logger";
import { BookmarkRecord, ScraperRunPaths, TrackerDefinition } from "./watcher.types";

const ensureDir = async (target: string) => {
  await fs.mkdir(target, { recursive: true });
};

export const createScraperRunPaths = async (watcherId: string): Promise<ScraperRunPaths> => {
  const runDir = path.join(config.watcherRuntimeDir, watcherId, randomUUID());
  const torrentDir = path.join(runDir, "torrent");
  await ensureDir(torrentDir);
  return {
    runDir,
    cookiesPath: path.join(runDir, "cookies.txt"),
    bookmarksPath: path.join(runDir, "bookmarks.json"),
    torrentDir,
  };
};

const scraperClient = axios.create({
  baseURL: config.wscraperServiceBaseUrl,
  timeout: config.watcherTimeoutMs,
});

const buildHeaders = () => {
  if (!config.wscraperServiceToken) {
    return undefined;
  }
  return {
    Authorization: `Bearer ${config.wscraperServiceToken}`,
  };
};

const request = async <T>(method: "GET" | "POST", url: string, body?: unknown) => {
  logger.info(
    {
      method,
      url,
      baseUrl: config.wscraperServiceBaseUrl,
    },
    "Calling wscraper service"
  );
  try {
    const response = await scraperClient.request<T>({
      method,
      url,
      data: body,
      headers: buildHeaders(),
    });
    return response.data;
  } catch (error) {
    if (axios.isAxiosError(error)) {
      const message =
        typeof error.response?.data === "object" && error.response?.data && "error" in error.response.data
          ? String((error.response.data as { error: string }).error)
          : error.message;
      throw new Error(`wscraper-service request failed: ${message}`);
    }
    throw error;
  }
};

export const listScraperTrackers = async () => {
  const response = await request<{ items: Array<{ key: string; label: string }> }>("GET", "/trackers");
  return response.items.map<TrackerDefinition>((item) => ({
    key: item.key,
    label: item.label,
    cliSiteKey: item.key,
    supportsRemoveBookmark: true,
  }));
};

export const runBookmarkFetch = async (
  trackerSiteKey: string,
  cookie: string,
  wishlistUrl?: string
) => {
  const response = await request<{ items: BookmarkRecord[] }>("POST", "/bookmarks", {
    tracker: trackerSiteKey,
    cookie,
    wishlistUrl,
  });
  return response.items;
};

export const runTorrentDownload = async (
  trackerSiteKey: string,
  cookie: string,
  bookmark: BookmarkRecord,
  outputDir: string,
  wishlistUrl?: string
) => {
  const response = await request<{ filename: string; contentBase64: string }>("POST", "/download", {
    tracker: trackerSiteKey,
    cookie,
    item: bookmark,
    wishlistUrl,
  });
  const targetPath = path.join(outputDir, response.filename);
  await fs.writeFile(targetPath, Buffer.from(response.contentBase64, "base64"));
  return targetPath;
};

export const runBookmarkRemove = async (
  trackerSiteKey: string,
  cookie: string,
  bookmark: BookmarkRecord,
  wishlistUrl?: string
) => {
  await request<{ ok: true }>("POST", "/remove-bookmark", {
    tracker: trackerSiteKey,
    cookie,
    item: bookmark,
    wishlistUrl,
  });
};

export const cleanupRunPaths = async (paths: ScraperRunPaths) => {
  try {
    await fs.rm(paths.runDir, { recursive: true, force: true });
  } catch (error) {
    logger.warn({ error, runDir: paths.runDir }, "Failed to cleanup watcher runtime directory");
  }
};
