const fs = require("fs");
const path = require("path");
const { execFileSync } = require("child_process");

const ROOT_DIR = path.resolve(__dirname, "..");
const OUTPUT_DIR = path.join(ROOT_DIR, "assets", "screenshots");
const FRONTEND_URL = stripTrailingSlash(process.env.QLOCKIFY_FRONTEND_URL || "http://localhost:5173");
const BACKEND_URL = stripTrailingSlash(process.env.QLOCKIFY_BACKEND_URL || "http://localhost:8000");
const BACKEND_REPO = path.resolve(
  ROOT_DIR,
  process.env.QLOCKIFY_BACKEND_REPO || path.join("..", "qlockify-backend"),
);
const ADMIN_USERNAME = process.env.QLOCKIFY_ADMIN_USERNAME || "09900000000";
const ADMIN_PASSWORD = process.env.QLOCKIFY_ADMIN_PASSWORD || "QlockifyLocalAdmin!2026";
const CHROME_EXECUTABLE = process.env.CHROME_EXECUTABLE || process.env.QLOCKIFY_CHROME_PATH || "";
const CAPTURE_ADMIN = process.env.QLOCKIFY_CAPTURE_ADMIN === "1";
const CLEAN_OUTPUT = process.env.QLOCKIFY_CLEAN_SCREENSHOTS !== "0";

function printHelp() {
  console.log(`
Usage:
  node scripts/capture-screenshots.js

Required:
  - Qlockify frontend running at ${FRONTEND_URL}
  - Qlockify backend running at ${BACKEND_URL}
  - npm install run inside scripts/ so Playwright is available

Useful environment variables:
  QLOCKIFY_FRONTEND_URL       Frontend URL. Default: http://localhost:5173
  QLOCKIFY_BACKEND_URL        Backend URL. Default: http://localhost:8000
  QLOCKIFY_BACKEND_REPO       Local backend repo path. Default: ../qlockify-backend
  QLOCKIFY_DEMO_TOKEN_FILE    JSON token file with access, refresh, workspace_id, expires_at
  QLOCKIFY_CAPTURE_ADMIN=1    Also capture Django admin screenshots
  QLOCKIFY_ADMIN_USERNAME     Local admin mobile for admin screenshots
  QLOCKIFY_ADMIN_PASSWORD     Local admin password for admin screenshots
  QLOCKIFY_CHROME_PATH        Optional installed Chrome executable
  QLOCKIFY_CLEAN_SCREENSHOTS=0 Do not delete old PNG screenshots before capture

The script does not start frontend, backend, PostgreSQL, Redis, or Celery.
`);
}

if (process.argv.includes("--help") || process.argv.includes("-h")) {
  printHelp();
  process.exit(0);
}

function stripTrailingSlash(value) {
  return value.replace(/\/+$/, "");
}

function wait(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function ensureDir(dir) {
  fs.mkdirSync(dir, { recursive: true });
}

function cleanScreenshots() {
  if (!CLEAN_OUTPUT || !fs.existsSync(OUTPUT_DIR)) return;
  for (const file of fs.readdirSync(OUTPUT_DIR)) {
    if (file.endsWith(".png")) {
      fs.rmSync(path.join(OUTPUT_DIR, file), { force: true });
    }
  }
}

async function assertReachable(url, label) {
  try {
    const response = await fetch(url, { method: "GET" });
    if (!response.ok && response.status >= 500) {
      throw new Error(`${label} returned ${response.status}`);
    }
  } catch (error) {
    throw new Error(`${label} is not reachable at ${url}. Start the service first. ${error.message}`);
  }
}

async function fetchJson(url, options = {}) {
  const response = await fetch(url, options);
  const body = await response.text();
  if (!response.ok) {
    throw new Error(`${url} failed: ${response.status} ${body}`);
  }
  return body ? JSON.parse(body) : {};
}

function pythonExecutable() {
  const windowsVenv = path.join(BACKEND_REPO, ".venv", "Scripts", "python.exe");
  const unixVenv = path.join(BACKEND_REPO, ".venv", "bin", "python");
  if (fs.existsSync(windowsVenv)) return windowsVenv;
  if (fs.existsSync(unixVenv)) return unixVenv;
  return "python";
}

function runDjangoShell(code) {
  if (!fs.existsSync(path.join(BACKEND_REPO, "manage.py"))) {
    throw new Error(`Backend repo was not found at ${BACKEND_REPO}`);
  }

  const output = execFileSync(pythonExecutable(), ["manage.py", "shell", "-c", code], {
    cwd: BACKEND_REPO,
    encoding: "utf8",
    stdio: ["ignore", "pipe", "pipe"],
  });
  return output.trim().split(/\r?\n/).filter(Boolean).at(-1);
}

function getDemoFromDjango() {
  const code = `
import json
from apps.demos.models import DemoEnvironment
from apps.users.services.auth import get_tokens_for_user
env = DemoEnvironment.objects.select_related("owner_user", "workspace").filter(status="active").order_by("-created_at").first()
if env is None:
    raise SystemExit("No active demo environment exists. Start one from the app or clear demo rate limit.")
print(json.dumps({**get_tokens_for_user(env.owner_user), "workspace_id": str(env.workspace_id), "expires_at": env.expires_at.isoformat(), "owner_user_id": str(env.owner_user_id)}))
`;
  return JSON.parse(runDjangoShell(code));
}

function prepareAdminFixtures(demo) {
  if (!CAPTURE_ADMIN) return;
  const adminUsername = JSON.stringify(ADMIN_USERNAME);
  const adminPassword = JSON.stringify(ADMIN_PASSWORD);
  const ownerId = JSON.stringify(demo.owner_user_id || "");
  const code = `
from django.contrib.auth import get_user_model
from apps.users.models import UserSocialAccount
User = get_user_model()
admin, _ = User.objects.update_or_create(
    mobile=${adminUsername},
    defaults={
        "first_name": "Showcase",
        "last_name": "Admin",
        "email": "showcase-admin@qlockify.local",
        "is_staff": True,
        "is_superuser": True,
        "is_active": True,
    },
)
admin.set_password(${adminPassword})
admin.save(update_fields=["password", "first_name", "last_name", "email", "is_staff", "is_superuser", "is_active"])
owner_id = ${ownerId}
if owner_id:
    owner = User.objects.filter(id=owner_id).first()
    if owner:
        UserSocialAccount.objects.update_or_create(
            provider="google",
            provider_user_id="showcase-demo-google",
            defaults={
                "user": owner,
                "email": owner.email or "demo-owner@demo.qlockify.local",
                "avatar_url": "https://qlockify.ir/favicon.svg",
            },
        )
print("ok")
`;
  runDjangoShell(code);
}

async function readDemoTokenFile() {
  const tokenFile = process.env.QLOCKIFY_DEMO_TOKEN_FILE;
  if (!tokenFile || !fs.existsSync(tokenFile)) return null;
  const token = JSON.parse(fs.readFileSync(tokenFile, "utf8").replace(/^\uFEFF/, ""));
  if (token?.access && token?.refresh && token?.workspace_id) return token;
  return null;
}

async function resolveDemoSession() {
  const fileToken = await readDemoTokenFile();
  if (fileToken) return fileToken;

  try {
    return await fetchJson(`${BACKEND_URL}/api/demo/start/`, { method: "POST" });
  } catch (error) {
    console.warn(`Demo start failed, trying latest active demo from Django shell: ${error.message}`);
    return getDemoFromDjango();
  }
}

async function settle(page, delay = 2000) {
  await page.waitForLoadState("domcontentloaded");
  await page.waitForLoadState("networkidle", { timeout: 12000 }).catch(() => {});
  await page
    .waitForFunction(
      () => {
        const text = document.body.innerText || "";
        const skeletons = document.querySelectorAll(
          '[class*="animate-pulse"], [class*="skeleton"], [aria-busy="true"]',
        ).length;
        return skeletons === 0 && !/Loading|در حال|بارگذاری|لطفا صبر/i.test(text);
      },
      { timeout: 12000 },
    )
    .catch(() => {});
  await wait(delay);
}

async function saveScreenshot(page, name, { fullPage = false, delay = 1000 } = {}) {
  await settle(page, delay);
  await page.screenshot({ path: path.join(OUTPUT_DIR, name), fullPage });
  console.log(`saved ${name}`);
}

async function openAppRoute(page, route, delay = 2500) {
  await page.goto(`${FRONTEND_URL}${route}`, { waitUntil: "domcontentloaded" });
  await settle(page, delay);
}

async function clickRoleButton(page, names, timeout = 5000) {
  for (const name of names) {
    const button = page.getByRole("button", { name }).first();
    if (await button.click({ timeout }).then(() => true).catch(() => false)) {
      await settle(page, 1200);
      return true;
    }
  }
  return false;
}

async function clickSelector(page, selectors, timeout = 5000) {
  for (const selector of selectors) {
    const locator = page.locator(selector).first();
    if (await locator.click({ timeout }).then(() => true).catch(() => false)) {
      await settle(page, 1200);
      return true;
    }
  }
  return false;
}

async function createBrowser() {
  const { chromium } = require("playwright");
  const options = { headless: true };
  if (CHROME_EXECUTABLE) {
    options.executablePath = CHROME_EXECUTABLE;
  }
  return chromium.launch(options);
}

async function createPublicContext(browser) {
  const context = await browser.newContext({
    viewport: { width: 1440, height: 900 },
    colorScheme: "dark",
    locale: "fa-IR",
    timezoneId: "Asia/Tehran",
  });
  await context.addInitScript(() => {
    localStorage.setItem("language", "fa");
    localStorage.setItem("theme", "dark");
  });
  return context;
}

async function createAppContext(browser, demo, viewport = { width: 1440, height: 900 }) {
  const context = await browser.newContext({
    viewport,
    colorScheme: "dark",
    locale: "fa-IR",
    timezoneId: "Asia/Tehran",
  });
  await context.addInitScript(({ access, refresh, expiresAt }) => {
    localStorage.setItem("language", "fa");
    localStorage.setItem("theme", "dark");
    localStorage.setItem("accessToken", access);
    localStorage.setItem("refreshToken", refresh);
    localStorage.setItem("demoExpiresAt", expiresAt || new Date(Date.now() + 86400000).toISOString());
  }, { access: demo.access, refresh: demo.refresh, expiresAt: demo.expires_at });
  return context;
}

async function getChartUserId(demo) {
  const chart = await fetchJson(
    `${BACKEND_URL}/api/reports/chart/?workspace=${demo.workspace_id}&period=this_month&language=fa`,
    { headers: { Authorization: `Bearer ${demo.access}` } },
  );
  const ownerSeries =
    chart.series?.find((series) => series.user?.name === "Demo Owner") ||
    chart.series?.find((series) => series.user);
  return ownerSeries?.user?.id || null;
}

async function capturePublicScreenshots(browser) {
  const context = await createPublicContext(browser);
  const page = await context.newPage();

  await openAppRoute(page, "/", 2400);
  await saveScreenshot(page, "01-landing.png");

  await openAppRoute(page, "/about", 2400);
  await saveScreenshot(page, "02-about.png", { fullPage: true });

  await openAppRoute(page, "/auth", 2400);
  await saveScreenshot(page, "03-auth-mobile.png");

  await context.close();
}

async function captureAppScreenshots(browser, demo) {
  const context = await createAppContext(browser, demo);
  const page = await context.newPage();

  await openAppRoute(page, "/timesheet", 4500);
  await saveScreenshot(page, "10-timesheet.png");
  if (await clickRoleButton(page, [/تعرفه.*من/i, /My rates/i])) {
    await saveScreenshot(page, "11-my-rates-panel.png");
    await page.keyboard.press("Escape");
    await wait(800);
  }

  await openAppRoute(page, "/reports?tab=table", 4500);
  await clickRoleButton(page, [/جدول/i, /Table/i]);
  await saveScreenshot(page, "20-reports-table.png");

  const chartUserId = await getChartUserId(demo);
  const chartRoute = chartUserId
    ? `/reports?tab=chart&period=this_month&user=${encodeURIComponent(chartUserId)}`
    : "/reports?tab=chart&period=this_month";
  await openAppRoute(page, chartRoute, 7000);
  await clickRoleButton(page, [/نمودار/i, /Chart/i]);
  await page.evaluate(() => {
    for (const el of document.querySelectorAll(".overflow-x-auto")) {
      el.scrollLeft = -el.scrollWidth;
    }
  });
  await wait(1200);
  await saveScreenshot(page, "21-reports-chart.png");

  await openAppRoute(page, "/clients", 3500);
  await saveScreenshot(page, "30-clients.png");
  if (await clickRoleButton(page, [/افزودن مشتری/i, /افزودن/i, /Create/i, /Add/i])) {
    await saveScreenshot(page, "31-client-create-modal.png");
    await page.keyboard.press("Escape");
    await wait(800);
  }
  if (await clickSelector(page, ['button[title="ویرایش"]', 'button[title="Edit"]', "button:has(svg.lucide-edit-2)"])) {
    await saveScreenshot(page, "32-client-edit-modal.png");
    await page.keyboard.press("Escape");
    await wait(800);
  }

  await openAppRoute(page, "/projects", 3500);
  await saveScreenshot(page, "40-projects.png");
  if (await clickSelector(page, ['button[title="ایجاد پروژه جدید"]', 'button[title="Create new project"]'])) {
    await saveScreenshot(page, "41-project-create-modal.png");
    await page.keyboard.press("Escape");
    await wait(800);
  }
  if (await clickRoleButton(page, [/پروژه.*نرخ/i, /Projects and rates/i])) {
    await saveScreenshot(page, "42-project-access-modal.png", { delay: 2200 });
    await page.keyboard.press("Escape");
    await wait(800);
  }

  await openAppRoute(page, "/tags", 3500);
  await saveScreenshot(page, "50-tags.png");
  if (await clickSelector(page, ['button[title="ایجاد تگ"]', 'button[title="Create Tag"]', "button:has(svg.lucide-plus)"])) {
    await saveScreenshot(page, "51-tags-create-modal.png");
    await page.keyboard.press("Escape");
    await wait(800);
  }

  await openAppRoute(page, "/workspaces", 3500);
  await saveScreenshot(page, "60-workspaces.png");
  if (demo.workspace_id) {
    await openAppRoute(page, `/workspaces/${demo.workspace_id}/edit`, 4500);
    await saveScreenshot(page, "61-workspace-edit-page.png");
  }

  await openAppRoute(page, "/profile", 3500);
  await saveScreenshot(page, "70-profile.png");
  if (await clickSelector(page, ["button:has(svg.lucide-edit-2)", 'button[title="ویرایش"]', 'button[title="Edit"]'])) {
    await saveScreenshot(page, "71-profile-edit-page.png");
  }

  await context.close();
}

async function loginAdmin(page) {
  await page.goto(`${BACKEND_URL}/admin/login/?next=/admin/`, { waitUntil: "domcontentloaded" });
  await settle(page, 800);
  await page.locator('input[name="username"]').fill(ADMIN_USERNAME);
  await page.locator('input[name="password"]').fill(ADMIN_PASSWORD);
  await page.locator('button[type="submit"], input[type="submit"]').first().click();
  await settle(page, 2200);
}

async function hideDebugToolbar(page) {
  await page
    .addStyleTag({
      content:
        '#djDebug, .djdt-hidden, [id^="djDebug"] { display: none !important; visibility: hidden !important; opacity: 0 !important; pointer-events: none !important; }',
    })
    .catch(() => {});
  await page
    .evaluate(() => {
      for (const node of document.querySelectorAll('#djDebug, .djdt-hidden, [id^="djDebug"]')) {
        node.remove();
      }
    })
    .catch(() => {});
}

async function saveAdminScreenshot(page, name, fullPage = false) {
  await hideDebugToolbar(page);
  await saveScreenshot(page, name, { fullPage });
}

async function captureAdminScreenshots(browser, demo) {
  if (!CAPTURE_ADMIN) {
    console.log("skipping admin screenshots; set QLOCKIFY_CAPTURE_ADMIN=1 to enable");
    return;
  }

  const context = await browser.newContext({
    viewport: { width: 1440, height: 1100 },
    colorScheme: "dark",
    locale: "en-US",
  });
  const page = await context.newPage();
  await loginAdmin(page);

  await saveAdminScreenshot(page, "80-admin-dashboard.png");
  await page.goto(`${BACKEND_URL}/admin/users/user/`, { waitUntil: "domcontentloaded" });
  await saveAdminScreenshot(page, "81-admin-users-list.png");

  const userId = process.env.QLOCKIFY_ADMIN_USER_ID || demo.owner_user_id;
  if (userId) {
    await page.goto(`${BACKEND_URL}/admin/users/user/${userId}/change/`, { waitUntil: "domcontentloaded" });
    await saveAdminScreenshot(page, "82-admin-user-detail-social-inline.png", true);
  }

  await page.goto(`${BACKEND_URL}/admin/workspaces/workspace/`, { waitUntil: "domcontentloaded" });
  await saveAdminScreenshot(page, "83-admin-workspaces-list.png");
  if (demo.workspace_id) {
    await page.goto(`${BACKEND_URL}/admin/workspaces/workspace/${demo.workspace_id}/change/`, { waitUntil: "domcontentloaded" });
    await saveAdminScreenshot(page, "84-admin-workspace-detail-members-inline.png", true);
  }
  await page.goto(`${BACKEND_URL}/admin/projects/project/`, { waitUntil: "domcontentloaded" });
  await saveAdminScreenshot(page, "85-admin-projects-list.png");
  await page.goto(`${BACKEND_URL}/admin/time_entries/timeentry/`, { waitUntil: "domcontentloaded" });
  await saveAdminScreenshot(page, "86-admin-time-entries-list.png");
  await page.goto(`${BACKEND_URL}/admin/users/usersocialaccount/`, { waitUntil: "domcontentloaded" });
  await saveAdminScreenshot(page, "87-admin-social-accounts-list.png");
  await page.goto(`${BACKEND_URL}/admin/contacts/contactsubmission/`, { waitUntil: "domcontentloaded" });
  await saveAdminScreenshot(page, "88-admin-contact-submissions-list.png");

  await context.close();
}

async function main() {
  ensureDir(OUTPUT_DIR);
  cleanScreenshots();

  await assertReachable(`${FRONTEND_URL}/`, "Frontend");
  await assertReachable(`${BACKEND_URL}/api/`, "Backend");

  const demo = await resolveDemoSession();
  prepareAdminFixtures(demo);

  const browser = await createBrowser();
  try {
    await capturePublicScreenshots(browser);
    await captureAppScreenshots(browser, demo);
    await captureAdminScreenshots(browser, demo);
  } finally {
    await browser.close();
  }
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});
