import type { Express } from "express";
import { createServer, type Server } from "http";
import { storage } from "./storage";
import { insertArticleSchema } from "@shared/schema";
import { seedDatabase } from "./seed";
import { generateDailyHoroscopes, getHoroscopesForToday, getOrGenerateHoroscope } from "./horoscope-generator";
import { analyzeAllArticleImages, getCachedFocalPoints } from "./focal-point";
import { optimizeImage } from "./image-optimizer";
import { getAuthUrl, exchangeCodeForTokens, isConnected, fetchGalleryFromDropbox, getValidAccessToken } from "./dropbox";
import multer from "multer";
import path from "path";
import fs from "fs";
import https from "https";

const apiCache = new Map<string, { data: any; timestamp: number }>();
function getCached<T>(key: string, ttlMs: number): T | null {
  const entry = apiCache.get(key);
  if (entry && Date.now() - entry.timestamp < ttlMs) return entry.data as T;
  return null;
}
function setCache(key: string, data: any) {
  apiCache.set(key, { data, timestamp: Date.now() });
}
function getStale<T>(key: string): T | null {
  const entry = apiCache.get(key);
  return entry ? (entry.data as T) : null;
}

const uploadDir = path.join(process.cwd(), "client/public/uploads");
if (!fs.existsSync(uploadDir)) {
  fs.mkdirSync(uploadDir, { recursive: true });
}

const upload = multer({
  storage: multer.diskStorage({
    destination: (_req, _file, cb) => cb(null, uploadDir),
    filename: (_req, file, cb) => {
      const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
      const ext = path.extname(file.originalname);
      cb(null, uniqueSuffix + ext);
    },
  }),
  limits: { fileSize: 10 * 1024 * 1024 },
  fileFilter: (_req, file, cb) => {
    const allowed = /jpeg|jpg|png|gif|webp/;
    const ext = allowed.test(path.extname(file.originalname).toLowerCase());
    const mime = allowed.test(file.mimetype);
    cb(null, ext && mime);
  },
});

export async function registerRoutes(
  httpServer: Server,
  app: Express
): Promise<Server> {
  await seedDatabase();

  generateDailyHoroscopes().catch((err) =>
    console.error("Background horoscope generation failed:", err.message)
  );

  storage.getArticles().then((articles) => {
    analyzeAllArticleImages(articles).catch(err => 
      console.log("[focal-point] Analysis skipped:", err.message)
    );
  });

  app.get("/api/focal-points", (_req, res) => {
    res.json(getCachedFocalPoints());
  });

  app.get("/api/articles", async (_req, res) => {
    const articles = await storage.getArticles();
    res.json(articles);
  });

  app.get("/api/articles/featured", async (_req, res) => {
    const articles = await storage.getFeaturedArticles();
    res.json(articles);
  });

  app.get("/api/articles/popular", async (req, res) => {
    const limit = parseInt(req.query.limit as string) || 6;
    const articles = await storage.getPopularArticles(limit);
    res.json(articles);
  });

  app.get("/api/articles/category/:category", async (req, res) => {
    const articles = await storage.getArticlesByCategory(req.params.category);
    res.json(articles);
  });

  app.get("/api/articles/:slug", async (req, res) => {
    const article = await storage.getArticleBySlug(req.params.slug);
    if (!article) {
      return res.status(404).json({ message: "Article not found" });
    }
    await storage.incrementViews(article.id);
    res.json({ ...article, views: article.views + 1 });
  });

  app.post("/api/articles", async (req, res) => {
    const parsed = insertArticleSchema.safeParse(req.body);
    if (!parsed.success) {
      return res.status(400).json({ message: "Invalid article data", errors: parsed.error.flatten() });
    }
    const existing = await storage.getArticleBySlug(parsed.data.slug);
    if (existing) {
      return res.status(409).json({ message: `Artikel "${existing.title}" existiert bereits (ID: ${existing.id})` });
    }
    const article = await storage.createArticle(parsed.data);
    res.status(201).json(article);
  });

  app.patch("/api/articles/:id", async (req, res) => {
    const id = parseInt(req.params.id);
    const partial = insertArticleSchema.partial().safeParse(req.body);
    if (!partial.success) {
      return res.status(400).json({ message: "Invalid article data", errors: partial.error.flatten() });
    }
    const article = await storage.updateArticle(id, partial.data);
    if (!article) {
      return res.status(404).json({ message: "Article not found" });
    }
    res.json(article);
  });

  app.delete("/api/articles/:id", async (req, res) => {
    const id = parseInt(req.params.id);
    await storage.deleteArticle(id);
    res.status(204).send();
  });

  app.post("/api/upload", upload.single("image"), async (req, res) => {
    if (!req.file) {
      return res.status(400).json({ message: "No file uploaded" });
    }
    try {
      const result = await optimizeImage(req.file.path);
      res.json({ url: result.webp, thumb: result.thumb });
    } catch (err) {
      const url = `/uploads/${req.file.filename}`;
      console.log("[image-optimizer] Optimization failed, using original:", (err as Error).message);
      res.json({ url });
    }
  });

  function bunnyFetch(path: string): Promise<any> {
    return new Promise((resolve, reject) => {
      const options = {
        hostname: "video.bunnycdn.com",
        path,
        method: "GET",
        headers: { "AccessKey": process.env.BUNNY_API_KEY || "", "Accept": "application/json" },
      };
      const req = https.request(options, (res) => {
        let body = "";
        res.on("data", (c: string) => (body += c));
        res.on("end", () => {
          try { resolve(JSON.parse(body)); } catch { reject(new Error(body)); }
        });
      });
      req.on("error", reject);
      req.end();
    });
  }

  const LIBRARY_ID = process.env.BUNNY_LIBRARY_ID || "476412";
  const CDN_HOST = process.env.BUNNY_CDN_HOST || "vz-7982dfc4-cc8.b-cdn.net";

  app.get("/api/videos", async (req, res) => {
    try {
      const page = parseInt(req.query.page as string) || 1;
      const perPage = parseInt(req.query.perPage as string) || 20;
      const search = req.query.search as string || "";
      const cacheKey = `videos_${page}_${perPage}_${search}`;
      const cached = getCached<any>(cacheKey, 30 * 60 * 1000);
      if (cached) return res.json(cached);

      let path = `/library/${LIBRARY_ID}/videos?page=${page}&itemsPerPage=${perPage}&orderBy=date`;
      if (search) path += `&search=${encodeURIComponent(search)}`;
      const data = await bunnyFetch(path);
      const videos = (data.items || []).map((v: any) => {
        const descTag = (v.metaTags || []).find((t: any) => t.property === "description");
        return {
          guid: v.guid,
          title: (v.title || "").replace(/\.mp4$/i, ""),
          description: descTag?.value || v.description || "",
          length: v.length,
          dateUploaded: v.dateUploaded,
          thumbnail: `https://${CDN_HOST}/${v.guid}/${v.thumbnailFileName || "thumbnail.jpg"}`,
          embedUrl: `https://player.mediadelivery.net/embed/${LIBRARY_ID}/${v.guid}`,
          hlsUrl: `https://${CDN_HOST}/${v.guid}/playlist.m3u8`,
          status: v.status,
          category: v.category || "",
        };
      });
      const result = { items: videos, totalItems: data.totalItems, currentPage: data.currentPage };
      setCache(cacheKey, result);
      res.json(result);
    } catch (err: any) {
      const stale = getStale<any>(`videos_${req.query.page || 1}_${req.query.perPage || 20}_${req.query.search || ""}`);
      if (stale) return res.json(stale);
      res.status(500).json({ message: err.message });
    }
  });

  app.get("/api/videos/:guid", async (req, res) => {
    try {
      const data = await bunnyFetch(`/library/${LIBRARY_ID}/videos/${req.params.guid}`);
      const descTag = (data.metaTags || []).find((t: any) => t.property === "description");
      res.json({
        guid: data.guid,
        title: (data.title || "").replace(/\.mp4$/i, ""),
        description: descTag?.value || data.description || "",
        length: data.length,
        dateUploaded: data.dateUploaded,
        thumbnail: `https://${CDN_HOST}/${data.guid}/${data.thumbnailFileName || "thumbnail.jpg"}`,
        embedUrl: `https://player.mediadelivery.net/embed/${LIBRARY_ID}/${data.guid}`,
        hlsUrl: `https://${CDN_HOST}/${data.guid}/playlist.m3u8`,
        status: data.status,
        category: data.category || "",
      });
    } catch (err: any) {
      res.status(500).json({ message: err.message });
    }
  });

  // Dropbox OAuth
  app.get("/api/dropbox/auth", (_req, res) => {
    const host = _req.headers.host || "news-folx-tv.replit.app";
    const protocol = _req.headers["x-forwarded-proto"] || "https";
    const redirectUri = `${protocol}://${host}/api/dropbox/callback`;
    const url = getAuthUrl(redirectUri);
    res.redirect(url);
  });

  app.get("/api/dropbox/callback", async (req, res) => {
    const code = req.query.code as string;
    if (!code) {
      return res.status(400).send("Manjka avtorizacijska koda.");
    }
    try {
      const host = req.headers.host || "news-folx-tv.replit.app";
      const protocol = req.headers["x-forwarded-proto"] || "https";
      const redirectUri = `${protocol}://${host}/api/dropbox/callback`;
      await exchangeCodeForTokens(code, redirectUri);
      res.send("<html><body style='background:#111;color:#fff;font-family:sans-serif;text-align:center;padding:60px'><h1>Dropbox povezan!</h1><p>Lahko zaprete to stran.</p></body></html>");
    } catch (err: any) {
      res.status(500).send(`Napaka: ${err.message}`);
    }
  });

  app.get("/api/dropbox/status", (_req, res) => {
    res.json({ connected: isConnected() });
  });

  const thumbCache = new Map<string, { data: Buffer; timestamp: number }>();
  const THUMB_CACHE_TTL = 30 * 60 * 1000;

  app.get("/api/gallery/thumb", async (req, res) => {
    const src = req.query.src as string;
    if (!src) return res.status(400).send("Missing src");

    const cached = thumbCache.get(src);
    if (cached && Date.now() - cached.timestamp < THUMB_CACHE_TTL) {
      res.set("Content-Type", "image/jpeg");
      res.set("Cache-Control", "public, max-age=1800");
      return res.send(cached.data);
    }

    try {
      const resp = await fetch(src);
      if (!resp.ok) return res.status(502).send("Upstream error");

      const arrayBuf = await resp.arrayBuffer();
      const fullBuf = Buffer.from(arrayBuf);

      const sharp = (await import("sharp")).default;
      const thumbBuf = await sharp(fullBuf)
        .resize(400, 400, { fit: "cover", position: "attention" })
        .jpeg({ quality: 70 })
        .toBuffer();

      thumbCache.set(src, { data: thumbBuf, timestamp: Date.now() });

      if (thumbCache.size > 300) {
        const oldest = [...thumbCache.entries()].sort((a, b) => a[1].timestamp - b[1].timestamp);
        for (let i = 0; i < 50; i++) thumbCache.delete(oldest[i][0]);
      }

      res.set("Content-Type", "image/jpeg");
      res.set("Cache-Control", "public, max-age=1800");
      res.send(thumbBuf);
    } catch (err: any) {
      res.status(500).send(err.message);
    }
  });

  app.post("/api/dropbox/refresh-gallery", async (_req, res) => {
    try {
      const images = await fetchGalleryFromDropbox();
      res.json({ count: images.length });
    } catch (err: any) {
      res.status(500).json({ message: err.message });
    }
  });

  // Gallery API - serves shuffled photos from Dropbox
  app.get("/api/gallery", async (_req, res) => {
    try {
      let data: any[] = [];

      if (isConnected()) {
        data = await fetchGalleryFromDropbox();
      }

      if (data.length === 0) {
        const galleryPath = path.join(process.cwd(), "server/gallery-data.json");
        data = JSON.parse(fs.readFileSync(galleryPath, "utf-8"));
      }

      const shuffled = [...data];
      for (let i = shuffled.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
      }
      res.json(shuffled);
    } catch (err: any) {
      res.json([]);
    }
  });

  // Horoscope API
  app.get("/api/horoscopes/today", async (_req, res) => {
    try {
      const horoscopes = await getHoroscopesForToday();
      res.json(horoscopes);
    } catch (err: any) {
      res.status(500).json({ message: err.message });
    }
  });

  app.get("/api/horoscopes/sign/:index", async (req, res) => {
    try {
      const signIndex = parseInt(req.params.index);
      if (isNaN(signIndex) || signIndex < 0 || signIndex > 11) {
        return res.status(400).json({ message: "Invalid sign index" });
      }
      const horoscope = await getOrGenerateHoroscope(signIndex);
      res.json(horoscope);
    } catch (err: any) {
      res.status(500).json({ message: err.message });
    }
  });

  app.post("/api/horoscopes/generate", async (_req, res) => {
    try {
      await generateDailyHoroscopes();
      const horoscopes = await getHoroscopesForToday();
      res.json({ generated: horoscopes.length, horoscopes });
    } catch (err: any) {
      res.status(500).json({ message: err.message });
    }
  });

  function parseRssItems(xml: string, maxAgeDays?: number): { title: string; link: string; source: string; pubDate: string }[] {
    const items: { title: string; link: string; source: string; pubDate: string }[] = [];
    const itemRegex = /<item>([\s\S]*?)<\/item>/g;
    let match;
    while ((match = itemRegex.exec(xml)) !== null && items.length < 10) {
      const block = match[1];
      const title = block.match(/<title>(.*?)<\/title>/)?.[1]?.replace(/<!\[CDATA\[|\]\]>/g, "") || "";
      const link = block.match(/<link>(.*?)<\/link>/)?.[1] || "";
      const source = block.match(/<source[^>]*>(.*?)<\/source>/)?.[1]?.replace(/<!\[CDATA\[|\]\]>/g, "") || "";
      const pubDateRaw = block.match(/<pubDate>(.*?)<\/pubDate>/)?.[1] || "";
      let pubDate = "";
      try {
        const d = new Date(pubDateRaw);
        if (maxAgeDays !== undefined) {
          const ageMs = Date.now() - d.getTime();
          if (ageMs > maxAgeDays * 24 * 3600000) continue;
        }
        const diffH = Math.floor((Date.now() - d.getTime()) / 3600000);
        if (diffH < 1) pubDate = "Gerade eben";
        else if (diffH < 24) pubDate = `vor ${diffH} Std.`;
        else pubDate = `vor ${Math.floor(diffH / 24)} T.`;
      } catch { pubDate = ""; }
      if (title && link) items.push({ title, link, source, pubDate });
    }
    return items;
  }

  async function fetchRssFeed(rssUrl: string): Promise<string> {
    const resp = await fetch(rssUrl, {
      headers: {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        "Accept-Language": "de-DE,de;q=0.9,en;q=0.5",
        "Cache-Control": "no-cache",
      },
      redirect: "follow",
    });
    if (!resp.ok) throw new Error(`RSS fetch failed: ${resp.status}`);
    return await resp.text();
  }

  app.get("/api/news-feed", async (_req, res) => {
    const cacheKey = "news-feed";
    const cached = getCached<any[]>(cacheKey, 15 * 60 * 1000);
    if (cached) return res.json(cached);

    try {
      const topics = ["Volksmusik", "Schlager+Musik", "Oberkrainer"];
      const topic = topics[Math.floor(Date.now() / 3600000) % topics.length];
      const rssUrl = `https://news.google.com/rss/search?q=${topic}&hl=de&gl=DE&ceid=DE:de`;
      const xml = await fetchRssFeed(rssUrl);
      const items = parseRssItems(xml, 14);
      if (items.length > 0) setCache(cacheKey, items);
      res.json(items);
    } catch (err: any) {
      console.log("[news-feed] RSS fetch error:", err.message);
      const stale = getStale<any[]>(cacheKey);
      res.json(stale || []);
    }
  });

  app.get("/api/breaking-news", async (_req, res) => {
    const cacheKey = "breaking-news";
    const cached = getCached<any[]>(cacheKey, 15 * 60 * 1000);
    if (cached) return res.json(cached);

    try {
      const topics = ["Nachrichten+Deutschland", "Nachrichten+Oesterreich", "Nachrichten+Europa", "Wirtschaft+aktuell", "Sport+aktuell"];
      const topic = topics[Math.floor(Date.now() / 3600000) % topics.length];
      const rssUrl = `https://news.google.com/rss/search?q=${topic}&hl=de&gl=DE&ceid=DE:de`;
      const xml = await fetchRssFeed(rssUrl);
      const items = parseRssItems(xml, 1);
      if (items.length > 0) setCache(cacheKey, items);
      res.json(items);
    } catch (err: any) {
      console.log("[breaking-news] RSS fetch error:", err.message);
      const stale = getStale<any[]>(cacheKey);
      res.json(stale || []);
    }
  });

  return httpServer;
}
