import fs from "fs";
import path from "path";

const TOKEN_PATH = path.join(process.cwd(), "server/dropbox-token.json");
const GALLERY_ROOT = "/family room/photos/oddaje/izvajalci selekcija";

interface DropboxTokens {
  access_token: string;
  refresh_token: string;
  expires_at: number;
  app_key?: string;
  app_secret?: string;
}

interface GalleryImage {
  folder: string;
  fileName: string;
  thumb: string;
  large: string;
  full: string;
  artist: string;
}

function extractArtistFromFileName(fileName: string): string {
  if (/^DSC\d/i.test(fileName) || /^IMG[\s_]\d/i.test(fileName)) return "";
  const withoutExt = fileName.replace(/\.\w+$/, "");
  const cleaned = withoutExt
    .replace(/\s*\(\d+\)\s*$/, "")
    .replace(/\s*\d+\s*$/, "")
    .replace(/\s*\(\d+\)\s*$/, "");
  return cleaned.trim();
}

function getAppKey(): string {
  const tokens = loadTokens();
  return tokens?.app_key || process.env.DROPBOX_APP_KEY || "";
}

function getAppSecret(): string {
  const tokens = loadTokens();
  return tokens?.app_secret || process.env.DROPBOX_APP_SECRET || "";
}

export function getAuthUrl(redirectUri: string): string {
  const params = new URLSearchParams({
    client_id: getAppKey(),
    response_type: "code",
    redirect_uri: redirectUri,
    token_access_type: "offline",
  });
  return `https://www.dropbox.com/oauth2/authorize?${params.toString()}`;
}

export async function exchangeCodeForTokens(code: string, redirectUri: string): Promise<DropboxTokens> {
  const appKey = getAppKey();
  const appSecret = getAppSecret();
  const resp = await fetch("https://api.dropboxapi.com/oauth2/token", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      code,
      grant_type: "authorization_code",
      redirect_uri: redirectUri,
      client_id: appKey,
      client_secret: appSecret,
    }),
  });

  if (!resp.ok) {
    const err = await resp.text();
    throw new Error(`Token exchange failed: ${err}`);
  }

  const data = await resp.json();
  const tokens: DropboxTokens = {
    access_token: data.access_token,
    refresh_token: data.refresh_token,
    expires_at: Date.now() + data.expires_in * 1000,
    app_key: appKey,
    app_secret: appSecret,
  };

  fs.writeFileSync(TOKEN_PATH, JSON.stringify(tokens, null, 2));
  return tokens;
}

function loadTokens(): DropboxTokens | null {
  try {
    if (fs.existsSync(TOKEN_PATH)) {
      return JSON.parse(fs.readFileSync(TOKEN_PATH, "utf-8"));
    }
  } catch {}
  return null;
}

async function refreshAccessToken(refreshToken: string): Promise<DropboxTokens> {
  const appKey = getAppKey();
  const appSecret = getAppSecret();
  const resp = await fetch("https://api.dropboxapi.com/oauth2/token", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      grant_type: "refresh_token",
      refresh_token: refreshToken,
      client_id: appKey,
      client_secret: appSecret,
    }),
  });

  if (!resp.ok) {
    const err = await resp.text();
    throw new Error(`Token refresh failed: ${err}`);
  }

  const data = await resp.json();
  const tokens: DropboxTokens = {
    access_token: data.access_token,
    refresh_token: refreshToken,
    expires_at: Date.now() + data.expires_in * 1000,
    app_key: appKey,
    app_secret: appSecret,
  };

  fs.writeFileSync(TOKEN_PATH, JSON.stringify(tokens, null, 2));
  return tokens;
}

export async function getValidAccessToken(): Promise<string | null> {
  let tokens = loadTokens();
  if (!tokens) return null;

  if (Date.now() > tokens.expires_at - 60000) {
    try {
      tokens = await refreshAccessToken(tokens.refresh_token);
    } catch (err: any) {
      console.error("Token refresh failed:", err.message);
      return null;
    }
  }

  return tokens.access_token;
}

export function isConnected(): boolean {
  return loadTokens() !== null;
}

async function listFolder(accessToken: string, folderPath: string, recursive = false): Promise<any[]> {
  const entries: any[] = [];
  let cursor: string | null = null;
  let hasMore = true;

  while (hasMore) {
    const url = cursor
      ? "https://api.dropboxapi.com/2/files/list_folder/continue"
      : "https://api.dropboxapi.com/2/files/list_folder";

    const body = cursor
      ? { cursor }
      : { path: folderPath, recursive, limit: 2000 };

    const resp = await fetch(url, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${accessToken}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(body),
    });

    if (!resp.ok) {
      const err = await resp.text();
      throw new Error(`List folder failed: ${err}`);
    }

    const data = await resp.json();
    entries.push(...data.entries);
    hasMore = data.has_more;
    cursor = data.cursor;
  }

  return entries;
}

async function getTemporaryLinks(accessToken: string, paths: string[]): Promise<Map<string, string>> {
  const results = new Map<string, string>();
  const batchSize = 10;

  for (let i = 0; i < paths.length; i += batchSize) {
    const batch = paths.slice(i, i + batchSize);
    const promises = batch.map(async (p) => {
      try {
        const resp = await fetch("https://api.dropboxapi.com/2/files/get_temporary_link", {
          method: "POST",
          headers: {
            Authorization: `Bearer ${accessToken}`,
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ path: p }),
        });

        if (resp.ok) {
          const data = await resp.json();
          results.set(p.toLowerCase(), data.link);
        }
      } catch {}
    });
    await Promise.all(promises);
  }

  return results;
}


const galleryCache: { data: GalleryImage[]; timestamp: number } = { data: [], timestamp: 0 };
const CACHE_DURATION = 30 * 60 * 1000;

export async function fetchGalleryFromDropbox(): Promise<GalleryImage[]> {
  if (galleryCache.data.length > 0 && Date.now() - galleryCache.timestamp < CACHE_DURATION) {
    return galleryCache.data;
  }

  const accessToken = await getValidAccessToken();
  if (!accessToken) return [];

  try {
    const topEntries = await listFolder(accessToken, GALLERY_ROOT);
    const folders = topEntries.filter((e: any) => e[".tag"] === "folder");
    const images: GalleryImage[] = [];

    for (const folder of folders) {
      try {
        const folderName = folder.name;
        const folderEntries = await listFolder(accessToken, folder.path_lower);
        const imageFiles = folderEntries.filter(
          (e: any) => e[".tag"] === "file" && /\.(jpg|jpeg|png|webp|gif)$/i.test(e.name)
        );

        const paths = imageFiles.map((f: any) => f.path_lower);
        const tempLinks = await getTemporaryLinks(accessToken, paths);

        for (const file of imageFiles) {
          const link = tempLinks.get(file.path_lower) || "";
          if (link) {
            images.push({
              folder: folderName,
              fileName: file.name,
              thumb: link,
              large: link,
              full: link,
              artist: extractArtistFromFileName(file.name),
            });
          }
        }
      } catch (err: any) {
        console.error(`Folder ${folder.name} error:`, err.message);
      }
    }

    if (images.length > 0) {
      galleryCache.data = images;
      galleryCache.timestamp = Date.now();

      const galleryPath = path.join(process.cwd(), "server/gallery-data.json");
      fs.writeFileSync(galleryPath, JSON.stringify(images, null, 2));
    }

    return images;
  } catch (err: any) {
    console.error("Dropbox gallery fetch error:", err.message);
    return [];
  }
}
