import express from "express";
import cors from "cors";
import path from "path";
import fs from "fs";
import { fileURLToPath } from "url";
import puppeteer from "puppeteer";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const app = express();
const PORT = 3000;
const DB_FILE = path.join(__dirname, "database.json");

app.use(express.json());
app.use(cors());
app.use(express.static(path.join(__dirname, "public")));

// Inisialisasi Database JSON Lokal
function getDB() {
  if (!fs.existsSync(DB_FILE)) {
    const initialData = {
      config: { stravaSession: "" },
      athletes: [],
      activities: [],
    };
    fs.writeFileSync(DB_FILE, JSON.stringify(initialData, null, 2));
    return initialData;
  }
  return JSON.parse(fs.readFileSync(DB_FILE, "utf-8"));
}

function saveDB(data) {
  fs.writeFileSync(DB_FILE, JSON.stringify(data, null, 2));
}

// ====================== API AUTENTIKASI / SESI ======================
app.post("/api/save-session", (req, res) => {
  const { sessionCookie } = req.body;
  if (!sessionCookie) return res.status(400).json({ error: "Cookie kosong." });
  const db = getDB();
  db.config.stravaSession = sessionCookie.trim();
  saveDB(db);
  return res.json({ success: true });
});

app.get("/api/session-status", (req, res) => {
  const db = getDB();
  return res.json({ hasSession: Boolean(db.config.stravaSession) });
});

// ====================== API CRUD ATLET ======================
app.get("/api/athletes", (req, res) => {
  const db = getDB();
  res.json(db.athletes);
});

app.post("/api/athletes", (req, res) => {
  const { name, team, category } = req.body;
  if (!name) return res.status(400).json({ error: "Nama atlet wajib diisi." });

  const db = getDB();
  const newAthlete = {
    id: "ath_" + Date.now(),
    name: name.trim(),
    team: (team || "-").trim(),
    category: category || "All",
    createdAt: new Date().toISOString(),
  };

  db.athletes.push(newAthlete);
  saveDB(db);
  res.json({ success: true, athlete: newAthlete });
});

app.put("/api/athletes/:id", (req, res) => {
  const { id } = req.params;
  const { name, team, category } = req.body;
  const db = getDB();

  const index = db.athletes.findIndex((a) => a.id === id);
  if (index === -1)
    return res.status(404).json({ error: "Atlet tidak ditemukan." });

  db.athletes[index] = {
    ...db.athletes[index],
    name: name ? name.trim() : db.athletes[index].name,
    team: team !== undefined ? team.trim() : db.athletes[index].team,
    category: category || db.athletes[index].category,
  };

  saveDB(db);
  res.json({ success: true, athlete: db.athletes[index] });
});

app.delete("/api/athletes/:id", (req, res) => {
  const { id } = req.params;
  const db = getDB();
  db.athletes = db.athletes.filter((a) => a.id !== id);
  db.activities = db.activities.filter((act) => act.athleteId !== id);
  saveDB(db);
  res.json({ success: true });
});

// ====================== API CRUD AKTIVITAS ======================
app.get("/api/activities", (req, res) => {
  const db = getDB();
  res.json(db.activities);
});

app.post("/api/activities", (req, res) => {
  const {
    athleteId,
    title,
    distanceKm,
    elevationM,
    timeStr,
    paceStr,
    stravaUrl,
  } = req.body;
  if (!athleteId)
    return res.status(400).json({ error: "Pilih atlet terlebih dahulu." });

  const db = getDB();
  const athlete = db.athletes.find((a) => a.id === athleteId);
  if (!athlete)
    return res.status(404).json({ error: "Atlet tidak ditemukan." });

  const newActivity = {
    id: "act_" + Date.now(),
    athleteId,
    athleteName: athlete.name,
    title: title || "Aktivitas Strava",
    distanceKm: parseFloat(distanceKm) || 0,
    elevationM: parseFloat(elevationM) || 0,
    timeStr: timeStr || "-",
    paceStr: paceStr || "-",
    stravaUrl: stravaUrl || "",
    date: new Date().toISOString(),
  };

  db.activities.push(newActivity);
  saveDB(db);
  res.json({ success: true, activity: newActivity });
});

app.put("/api/activities/:id", (req, res) => {
  const { id } = req.params;
  const { title, distanceKm, elevationM, timeStr, paceStr } = req.body;
  const db = getDB();

  const idx = db.activities.findIndex((a) => a.id === id);
  if (idx === -1)
    return res.status(404).json({ error: "Aktivitas tidak ditemukan." });

  db.activities[idx] = {
    ...db.activities[idx],
    title: title || db.activities[idx].title,
    distanceKm:
      distanceKm !== undefined
        ? parseFloat(distanceKm)
        : db.activities[idx].distanceKm,
    elevationM:
      elevationM !== undefined
        ? parseFloat(elevationM)
        : db.activities[idx].elevationM,
    timeStr: timeStr || db.activities[idx].timeStr,
    paceStr: paceStr || db.activities[idx].paceStr,
  };

  saveDB(db);
  res.json({ success: true, activity: db.activities[idx] });
});

app.delete("/api/activities/:id", (req, res) => {
  const { id } = req.params;
  const db = getDB();
  db.activities = db.activities.filter((a) => a.id !== id);
  saveDB(db);
  res.json({ success: true });
});

// ====================== API LEADERBOARD PUBLIK ======================
app.get("/api/leaderboard", (req, res) => {
  const db = getDB();
  const statsMap = {};

  db.athletes.forEach((ath) => {
    statsMap[ath.id] = {
      id: ath.id,
      name: ath.name,
      team: ath.team,
      category: ath.category,
      totalDistance: 0,
      totalElevation: 0,
      activityCount: 0,
    };
  });

  db.activities.forEach((act) => {
    if (statsMap[act.athleteId]) {
      statsMap[act.athleteId].totalDistance += act.distanceKm;
      statsMap[act.athleteId].totalElevation += act.elevationM;
      statsMap[act.athleteId].activityCount += 1;
    }
  });

  const list = Object.values(statsMap);

  const distanceRank = [...list]
    .sort((a, b) => b.totalDistance - a.totalDistance)
    .map((item, idx) => ({
      ...item,
      rank: idx + 1,
      totalDistanceFormatted: item.totalDistance.toFixed(2),
    }));

  const climbRank = [...list]
    .sort((a, b) => b.totalElevation - a.totalElevation)
    .map((item, idx) => ({
      ...item,
      rank: idx + 1,
      totalElevationFormatted: Math.round(item.totalElevation),
    }));

  res.json({
    distanceLeaderboard: distanceRank,
    climbLeaderboard: climbRank,
  });
});

// ====================== STRAVA PUPPETEER PARSER ======================
app.post("/api/parse-strava", async (req, res) => {
  let { url } = req.body;
  if (!url) return res.status(400).json({ error: "URL tidak boleh kosong." });

  const db = getDB();
  const savedStravaSession = db.config.stravaSession;

  let browser = null;
  try {
    browser = await puppeteer.launch({
      headless: "new",
      channel: "chrome",
      args: [
        "--no-sandbox",
        "--disable-setuid-sandbox",
        "--disable-blink-features=AutomationControlled",
      ],
    });

    const page = await browser.newPage();
    await page.setViewport({ width: 1440, height: 900 });
    await page.setUserAgent(
      "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
    );

    if (savedStravaSession) {
      await page.setCookie({
        name: "_strava4_session",
        value: savedStravaSession,
        domain: ".strava.com",
        path: "/",
        httpOnly: true,
        secure: true,
      });
    }

    await page.goto(url.trim(), { waitUntil: "networkidle2", timeout: 40000 });
    await new Promise((r) => setTimeout(r, 2000));

    const result = await page.evaluate(() => {
      const titleEl =
        document.querySelector('h1[data-testid="activity-name"]') ||
        document.querySelector(".activity-name") ||
        document.querySelector("h1");
      let title = titleEl ? titleEl.innerText.trim() : "Aktivitas Strava";
      title = title.replace(/[-|]\s*Strava.*$/i, "").trim();

      let distance = "0";
      let time = "-";
      let pace = "-";
      let elevation = "0";

      // 1. Ekstraksi Dasar (Cari di struktur tabel/list umum)
      const statElements = document.querySelectorAll(
        '.inline-stats > li, section[data-testid="activity-stats"] > div, .activity-stats .stat, .more-stats table tbody tr, .more-stats li',
      );

      statElements.forEach((el) => {
        // HINDARI SEGMEN & SPLITS
        if (
          el.closest(".splits-table") ||
          el.closest('[data-testid="splits-table"]') ||
          el.closest(".segments-list") ||
          el.closest('[data-testid="segment-efforts"]')
        )
          return;

        const text = (el.innerText || "").replace(/\n+/g, " ").trim();
        const lower = text.toLowerCase();

        if (
          (lower.includes("distance") || lower.includes("jarak")) &&
          distance === "0"
        ) {
          const m = text.match(/([0-9.,]+)\s*(?:km|mi)/i);
          if (m) distance = m[1].replace(",", ".");
        }
        if (
          (lower.includes("time") || lower.includes("waktu")) &&
          !lower.includes("elapsed") &&
          time === "-"
        ) {
          const m = text.match(/([0-9]+:[0-9]+(?::[0-9]+)?)/i);
          if (m) time = m[1];
        }
        if (
          (lower.includes("pace") ||
            lower.includes("speed") ||
            lower.includes("kecepatan")) &&
          pace === "-"
        ) {
          const m = text.match(/([0-9.:]+)\s*(\/km|\/mi|km\/h|mph)/i);
          if (m) pace = `${m[1]} ${m[2]}`;
        }
        if (
          (lower.includes("elevation") ||
            lower.includes("elev gain") ||
            lower.includes("tanjakan") ||
            lower.includes("elevasi")) &&
          elevation === "0"
        ) {
          const m = text.match(/([0-9.,]+)\s*(?:m|meter|ft)\b/i);
          if (m) elevation = m[1].replace(/,/g, "");
        }
      });

      // 2. PENYISIRAN MENDALAM KHUSUS ELEVASI
      // Strava Web baru sering menyembunyikan elevasi tanpa class yang jelas

      // A. Coba atribut resmi dari Strava React UI
      if (elevation === "0") {
        const testIdEl = document.querySelector(
          '[data-testid="elevation-gain"]',
        );
        if (
          testIdEl &&
          !testIdEl.closest(".splits-table") &&
          !testIdEl.closest(".segments-list")
        ) {
          const m = testIdEl.innerText.match(/([0-9.,]+)\s*(?:m|ft)/i);
          if (m) elevation = m[1].replace(/,/g, "");
        }
      }

      // B. Penyisiran manual dengan mencari label yang persis bertuliskan Elevation
      if (elevation === "0") {
        const allLabels = document.querySelectorAll(
          "th, span, div, p, .label, .stat-subtext",
        );

        for (let labelEl of allLabels) {
          // Tetap hindari tabel splits dan segmen
          if (
            labelEl.closest(".splits-table") ||
            labelEl.closest('[data-testid="splits-table"]') ||
            labelEl.closest(".segments-list") ||
            labelEl.closest('[data-testid="segment-efforts"]')
          )
            continue;

          const labelText = (labelEl.innerText || "").trim().toLowerCase();

          // Pastikan label tidak lebih dari 3 kata untuk menghindari paragraf deskripsi
          if (labelText.length > 0 && labelText.length <= 25) {
            if (
              labelText === "elevation" ||
              labelText === "elev gain" ||
              labelText === "tanjakan" ||
              labelText === "elevasi" ||
              labelText === "elevation gain"
            ) {
              // Ambil teks dari wadah utamanya (Parent) yang biasanya menggabungkan "Elevation" + "18 m"
              const parentText = (
                labelEl.parentElement.innerText || ""
              ).replace(/\n+/g, " ");
              const matchElev = parentText.match(
                /([0-9.,]+)\s*(?:m|meter|ft)\b/i,
              );

              if (matchElev) {
                elevation = matchElev[1].replace(/,/g, "");
                break;
              }
            }
          }
        }
      }

      return {
        title,
        distanceKm: parseFloat(distance) || 0,
        elevationM: parseFloat(elevation) || 0,
        timeStr: time,
        paceStr: pace,
      };
    });

    await browser.close();

    return res.json({
      title: result.title,
      distanceKm: result.distanceKm,
      elevationM: result.elevationM,
      timeStr: result.timeStr,
      paceStr: result.paceStr,
      raw: result,
    });
  } catch (error) {
    if (browser) await browser.close().catch(() => {});
    return res
      .status(500)
      .json({ error: "Gagal mengambil data Strava: " + error.message });
  }
});

app.listen(PORT, () => {
  console.log(`Server aktif:`);
  console.log(`- Publik: http://localhost:${PORT}`);
  console.log(`- Admin:  http://localhost:${PORT}/admin.html`);
});
