/**
 * End-to-End Production Hardening & Blog CMS Verification Script
 * Validates:
 * 1. MongoDB Collections & Indexes
 * 2. Category Module CRUD & Validation
 * 3. 2FA Trusted Device (30-day token) Lifecycle
 * 4. Mandatory SEO Title & Meta Description Preflight Blocking
 * 5. 4-Tier OG / X Social Metadata Fallbacks
 * 6. Atomic Draft Save & Update Live State Transitions
 * 7. Automatic Read-Time Calculation
 */

import { db } from "../lib/admin/db";
import { blogService, validatePostPreflight } from "../lib/blog-service";
import { calculateReadingStats } from "../lib/reading-time";

async function runHardeningTests() {
  console.log("\n============================================================");
  console.log("QUTO AI CMS — PRODUCTION HARDENING & ZERO-ERROR AUDIT");
  console.log("============================================================\n");

  let passes = 0;
  let failures = 0;

  function assert(condition: boolean, testName: string, detail?: string) {
    if (condition) {
      console.log(`[PASS] ${testName}`);
      passes++;
    } else {
      console.error(`[FAIL] ${testName}${detail ? `: ${detail}` : ""}`);
      failures++;
    }
  }

  try {
    // ------------------------------------------------------------
    // 1. Category Module Verification
    // ------------------------------------------------------------
    console.log("--- 1. Category System & MongoDB Persistence ---");
    const categories = await db.getCategories();
    assert(categories.length >= 5, "Database contains seeded or stored categories", `Found ${categories.length}`);

    const testSlug = `test-cat-${Date.now()}`;
    const newCategory = await db.createCategory({
      name: `Test Cat ${Date.now()}`,
      slug: testSlug,
      description: "Automated test category for CMS validation.",
    });
    assert(!!newCategory.id && newCategory.slug === testSlug, "Created test category in MongoDB");

    const fetchedCat = await db.getCategoryBySlug(testSlug);
    assert(fetchedCat?.id === newCategory.id, "Resolved category by slug from MongoDB");

    const updatedCat = await db.updateCategory(newCategory.id, {
      description: "Updated description for verification.",
    });
    assert(updatedCat.description === "Updated description for verification.", "Updated category details");

    const deleted = await db.deleteCategory(newCategory.id);
    assert(deleted, "Deleted test category safely");

    // ------------------------------------------------------------
    // 2. 2FA Trusted Device (30-Day Token) Lifecycle
    // ------------------------------------------------------------
    console.log("\n--- 2. 2FA Trusted Device Lifecycle ---");
    const testUserId = "usr-test-runner";
    const testToken = "secure_random_device_token_abc123xyz789";

    const trustedDevice = await db.createTrustedDevice({
      userId: testUserId,
      token: testToken,
      userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
      ipAddress: "127.0.0.1",
    });
    assert(!!trustedDevice.id, "Stored cryptographically hashed trusted device in MongoDB");

    const isValidDevice = await db.verifyTrustedDevice(testUserId, testToken);
    assert(isValidDevice === true, "Verified trusted device with valid token");

    const isInvalidDevice = await db.verifyTrustedDevice(testUserId, "wrong_token");
    assert(isInvalidDevice === false, "Rejected trusted device with incorrect token");

    const isNonExistentUser = await db.verifyTrustedDevice("non_existent_user", testToken);
    assert(isNonExistentUser === false, "Rejected trusted device for invalid user ID");

    await db.revokeAllUserTrustedDevices(testUserId);
    const isRevoked = await db.verifyTrustedDevice(testUserId, testToken);
    assert(isRevoked === false, "Revoked user trusted device successfully");

    // ------------------------------------------------------------
    // 3. Mandatory SEO Preflight Validation
    // ------------------------------------------------------------
    console.log("\n--- 3. Mandatory SEO Preflight Blocking ---");
    const postWithoutSeoTitle = {
      title: "Valid Post Title",
      slug: "valid-post-slug",
      contentHtml: "<p>Valid article body with enough content to read.</p>",
      seoTitle: "", // Missing
      metaDescription: "A valid meta description that explains what the article is about.",
    };
    const preflight1 = validatePostPreflight(postWithoutSeoTitle as any);
    assert(
      !preflight1.valid && preflight1.errors.some((e) => e.includes("SEO title is required")),
      "Preflight blocks publication when SEO title is empty"
    );

    const postWithoutMetaDesc = {
      title: "Valid Post Title",
      slug: "valid-post-slug",
      contentHtml: "<p>Valid article body with enough content to read.</p>",
      seoTitle: "Valid SEO Title Tag",
      metaDescription: "", // Missing
    };
    const preflight2 = validatePostPreflight(postWithoutMetaDesc as any);
    assert(
      !preflight2.valid && preflight2.errors.some((e) => e.includes("Meta description is required")),
      "Preflight blocks publication when Meta description is empty"
    );

    const fullyValidPost = {
      title: "Valid Post Title",
      slug: "valid-post-slug",
      contentHtml: "<p>Valid article body with enough content to read.</p>",
      seoTitle: "Valid SEO Title Tag",
      metaDescription: "A valid meta description that explains what the article is about.",
    };
    const preflight3 = validatePostPreflight(fullyValidPost as any);
    assert(preflight3.valid, "Preflight succeeds when both SEO title and meta description are provided");

    // ------------------------------------------------------------
    // 4. Automatic Reading Stats Calculation
    // ------------------------------------------------------------
    console.log("\n--- 4. Automatic Reading Stats Calculation ---");
    const shortText = "<p>Hello world. This is a short test post with only a few words.</p>";
    const shortStats = calculateReadingStats(shortText);
    assert(shortStats.words > 0 && shortStats.readTime === "1 min read", "Short content calculates as '1 min read'");

    // Generate ~600 words
    const longWords = Array.from({ length: 600 }).map((_, i) => `word${i}`).join(" ");
    const longText = `<p>${longWords}</p>`;
    const longStats = calculateReadingStats(longText);
    assert(longStats.words >= 600 && longStats.minutes === 3, "600 words calculates accurately to 3 min read (200 WPM)");

    // ------------------------------------------------------------
    // 5. Atomic Working Draft & Update Live Workflow
    // ------------------------------------------------------------
    console.log("\n--- 5. Atomic Draft & Update Live Lifecycle ---");
    const mockActor = {
      id: "actor-test-runner",
      name: "Master Admin",
      email: "admin@qutoai.com",
      role: "MASTER_ADMIN",
    };

    // Create Draft
    const testPost = await blogService.createDraft(
      {
        title: `E2E Hardening Test ${Date.now()}`,
        category: "Voice AI Guides",
      },
      mockActor
    );
    assert(!!testPost.id && testPost.status === "DRAFT", "Created initial draft article in MongoDB");

    // Add required fields
    await blogService.updateDraft(
      testPost.id,
      {
        contentHtml: "<p>Comprehensive guide to conversational voice AI latency architecture and WebRTC streams.</p>",
        seoTitle: "Voice AI Latency Guide | Quto AI",
        metaDescription: "Learn how to optimize end-to-end voice latency to under 300ms using SIP and WebSocket.",
        featuredImage: "/uploads/blog/2026/09/banner-test.webp",
        featuredImageAlt: "Architecture diagram",
        ogTitle: "Custom OG Latency Title",
        ogImage: "/uploads/blog/2026/09/og-test.webp",
        twitterTitle: "Custom Twitter Latency Title",
      },
      mockActor
    );

    // Initial Publish
    const publishedPost = await blogService.publish(testPost.id, mockActor);
    assert(publishedPost.status === "PUBLISHED", "Transitioned article status to PUBLISHED");
    assert(publishedPost.version >= 1, "Initial published version is >= 1");

    // Edit while published -> Must go to workingDraft, keeping public live version safe
    const initialLiveTitle = publishedPost.title;
    const workingDraft = await blogService.updateDraft(
      publishedPost.id,
      {
        title: "DRAFT NEW TITLE (Uncommitted)",
        featuredImage: "/uploads/blog/2026/09/updated-banner.webp",
      },
      mockActor
    );

    // Check DB record directly to verify public live version remains UNCHANGED
    const rawPostInDb = await db.getPostById(publishedPost.id);
    assert(rawPostInDb?.title === initialLiveTitle, "Public post title remains untouched while working changes exist");
    assert(rawPostInDb?.workingDraft?.title === "DRAFT NEW TITLE (Uncommitted)", "Working changes safely stored in workingDraft");
    assert(
      rawPostInDb?.workingDraft?.featuredImage === "/uploads/blog/2026/09/updated-banner.webp",
      "New featured image stored safely in workingDraft"
    );

    // Update Live -> Working draft committed to live, version incremented
    const updatedLivePost = await blogService.updateLive(publishedPost.id, mockActor);
    assert(updatedLivePost.title === "DRAFT NEW TITLE (Uncommitted)", "Live title committed upon Update Live");
    assert(
      updatedLivePost.featuredImage === "/uploads/blog/2026/09/updated-banner.webp",
      "Live featured image committed upon Update Live"
    );
    assert(updatedLivePost.version > publishedPost.version, "Article version incremented upon Update Live");
    assert(updatedLivePost.workingDraft === null || updatedLivePost.workingDraft === undefined, "workingDraft cleared after live update");
    assert(updatedLivePost.hasWorkingDraft === false, "hasWorkingDraft is false after live update");

    // Clean up test post using force=true
    await db.deletePost(publishedPost.id, true);
    const postGone = await db.getPostById(publishedPost.id);
    assert(postGone === null, "Cleaned up test post from MongoDB");

  } catch (err: any) {
    console.error("Test execution failed with exception:", err);
    failures++;
  }

  console.log("\n============================================================");
  console.log(`AUDIT RESULTS: ${passes} PASSED, ${failures} FAILED`);
  console.log("============================================================\n");

  process.exit(failures > 0 ? 1 : 0);
}

runHardeningTests();
