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

// Always ensure .env variables are loaded from disk if not already set
try {
  const envPath = path.resolve(process.cwd(), ".env");
  if (fs.existsSync(envPath)) {
    const envLines = fs.readFileSync(envPath, "utf-8").split("\n");
    for (const line of envLines) {
      const trimmed = line.trim();
      if (!trimmed || trimmed.startsWith("#")) continue;
      const eqIdx = trimmed.indexOf("=");
      if (eqIdx > 0) {
        const key = trimmed.substring(0, eqIdx).trim();
        let val = trimmed.substring(eqIdx + 1).trim();
        if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
          val = val.substring(1, val.length - 1);
        }
        if (process.env[key] === undefined) {
          process.env[key] = val;
        }
      }
    }
  }
} catch (e) {
  // Ignore
}

import prisma from "../app/db.server";
(globalThis as any).prisma = prisma;
global.prisma = prisma;
import {
  generateClientAssertion,
  redactToken,
  TokenExchangeResult,
} from "../app/services/token-manager.server";
import { encryptToken, decryptToken } from "../app/lib/security/encryption";

async function runTestSuite() {
  console.log("==================================================================");
  console.log("🚀 SHOPIPILOT AI: EXPIRING OFFLINE TOKENS & RECOVERY TEST SUITE");
  console.log("==================================================================\n");

  let passed = 0;
  let failed = 0;

  function assert(condition: boolean, testName: string, details?: string) {
    if (condition) {
      console.log(`✅ [PASS] ${testName}`);
      passed++;
    } else {
      console.error(`❌ [FAIL] ${testName}${details ? " - " + details : ""}`);
      failed++;
    }
  }

  // -------------------------------------------------------------------------
  // TEST 1: Database Schema & Column Verification
  // -------------------------------------------------------------------------
  console.log("--- 1. Testing Database Schema & Storage ---");
  try {
    const testDomain = "test-verification-store.myshopify.com";
    const now = new Date();
    const tokenExpiresAt = new Date(now.getTime() + 3600 * 1000);
    const refreshTokenExpiresAt = new Date(now.getTime() + 90 * 24 * 3600 * 1000);

    const rawAccessToken = "shpca_sample_access_token_12345678";
    const rawRefreshToken = "shprt_sample_refresh_token_87654321";

    const saved = await prisma.shop.upsert({
      where: { shopifyDomain: testDomain },
      update: {
        accessToken: encryptToken(rawAccessToken),
        refreshToken: encryptToken(rawRefreshToken),
        tokenExpiresAt,
        refreshTokenExpiresAt,
        tokenUpdatedAt: now,
      },
      create: {
        shopifyDomain: testDomain,
        accessToken: encryptToken(rawAccessToken),
        refreshToken: encryptToken(rawRefreshToken),
        tokenExpiresAt,
        refreshTokenExpiresAt,
        tokenUpdatedAt: now,
        scopes: "read_products,read_content",
      },
    });

    assert(Boolean(saved.id), "Store record created in MySQL database");
    assert(Boolean(saved.refreshTokenExpiresAt), "refreshTokenExpiresAt column exists and persisted");
    assert(Boolean(saved.tokenUpdatedAt), "tokenUpdatedAt column exists and persisted");

    // Decrypt and verify at-rest encryption
    const decryptedAccess = decryptToken(saved.accessToken);
    const decryptedRefresh = saved.refreshToken ? decryptToken(saved.refreshToken) : "";

    assert(saved.accessToken !== rawAccessToken, "Access token is encrypted at rest (AES-256)");
    assert(decryptedAccess === rawAccessToken, "Access token decrypts accurately");
    assert(decryptedRefresh === rawRefreshToken, "Refresh token decrypts accurately");

    // Clean up test shop
    await prisma.shop.deleteMany({ where: { shopifyDomain: testDomain } });
  } catch (err: any) {
    assert(false, "Database schema test exception", err.message);
  }

  // -------------------------------------------------------------------------
  // TEST 2: Token Redaction in Logs
  // -------------------------------------------------------------------------
  console.log("\n--- 2. Testing Token Redaction & Privacy ---");
  const sampleToken = "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";
  const redacted = redactToken(sampleToken);
  assert(!redacted.includes("99887766554433221100"), "Middle portion of token is never exposed in logs");
  assert(redacted.startsWith("shpca_") && redacted.endsWith("eeff"), "Safe prefix/suffix preview is maintained");
  assert(redactToken(null) === "[EMPTY]", "Null token safely formatted");

  // -------------------------------------------------------------------------
  // TEST 3: Client Credentials RS256 JWT Client Assertion
  // -------------------------------------------------------------------------
  console.log("\n--- 3. Testing Client Credentials Access (Private Key RSA Signing) ---");
  try {
    // Generate an RSA keypair for testing
    const { privateKey, publicKey } = crypto.generateKeyPairSync("rsa", {
      modulusLength: 2048,
      publicKeyEncoding: { type: "spki", format: "pem" },
      privateKeyEncoding: { type: "pkcs8", format: "pem" },
    });

    const clientId = "ff484160a48beda29e79144ee4e9a819";
    const endpoint = "https://my-store.myshopify.com/admin/oauth/access_token";
    const kid = "test_key_id_2026";

    const assertionJwt = generateClientAssertion(clientId, endpoint, privateKey, kid);
    const parts = assertionJwt.split(".");
    assert(parts.length === 3, "Client assertion generated valid 3-part compact JWT");

    const header = JSON.parse(Buffer.from(parts[0], "base64url").toString("utf8"));
    const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));

    assert(header.alg === "RS256", "JWT header specifies RS256 algorithm");
    assert(header.kid === kid, "JWT header contains Key ID (kid)");
    assert(payload.iss === clientId, "JWT issuer matches app client ID");
    assert(payload.sub === clientId, "JWT subject matches app client ID");
    assert(payload.aud === endpoint, "JWT audience matches shop token endpoint");
    assert(payload.exp > Math.floor(Date.now() / 1000), "JWT expiration set in future (+60s)");
    assert(Boolean(payload.jti), "JWT contains unique nonce (jti)");

    // Cryptographic signature verification with public key
    const verifier = crypto.createVerify("RSA-SHA256");
    verifier.update(`${parts[0]}.${parts[1]}`);
    const isValidSignature = verifier.verify(publicKey, parts[2], "base64url");
    assert(isValidSignature, "RS256 cryptographic signature verified successfully with public key");
  } catch (err: any) {
    assert(false, "Client assertion generation exception", err.message);
  }

  // -------------------------------------------------------------------------
  // TEST 4: Concurrency Lock / Mutex Deduplication
  // -------------------------------------------------------------------------
  console.log("\n--- 4. Testing Concurrency Mutex Lock (Race Condition Prevention) ---");
  try {
    const mockDomain = "concurrent-test.myshopify.com";
    const rawToken = "shpca_active_token_test";
    const validExpiry = new Date(Date.now() + 3600 * 1000); // 1 hour future

    await prisma.shop.upsert({
      where: { shopifyDomain: mockDomain },
      update: {
        accessToken: encryptToken(rawToken),
        refreshToken: encryptToken("shprt_sample_refresh_token"),
        tokenExpiresAt: validExpiry,
        refreshTokenExpiresAt: new Date(Date.now() + 90 * 24 * 3600 * 1000),
      },
      create: {
        shopifyDomain: mockDomain,
        accessToken: encryptToken(rawToken),
        refreshToken: encryptToken("shprt_sample_refresh_token"),
        tokenExpiresAt: validExpiry,
        refreshTokenExpiresAt: new Date(Date.now() + 90 * 24 * 3600 * 1000),
        scopes: "read_products",
      },
    });

    // Fire 10 simultaneous parallel requests to getValidAdminAccessToken
    const { getValidAdminAccessToken } = await import("../app/services/token-manager.server");
    const parallelCalls = Array.from({ length: 10 }, () => getValidAdminAccessToken(mockDomain));
    const results = await Promise.all(parallelCalls);

    const allIdentical = results.every((tok) => tok === rawToken);
    assert(allIdentical, "All 10 parallel concurrent requests safely returned identical valid access token");

    await prisma.shop.deleteMany({ where: { shopifyDomain: mockDomain } });
  } catch (err: any) {
    assert(false, "Concurrency lock test exception", err.message);
  }

  // -------------------------------------------------------------------------
  // Summary
  // -------------------------------------------------------------------------
  console.log("\n==================================================================");
  console.log(`TEST SUITE RESULTS: ${passed} PASSED, ${failed} FAILED`);
  console.log("==================================================================");

  if (failed > 0) {
    process.exit(1);
  }
}

runTestSuite()
  .catch((e) => {
    console.error("Test runner failed:", e);
    process.exit(1);
  })
  .finally(() => prisma.$disconnect());
