import { encrypt, decrypt } from '../src/lib/crypto/encryption';
import { verifyShopifyWebhookHmac, verifyShopifyOAuthQuery, isValidShopifyDomain } from '../src/lib/shopify/hmac';
import { hashMetaField, hashMetaPhone, hashMetaCountry, hashMetaZip } from '../src/lib/meta/hashing';
import { generateMetaXmlFeed, generateMetaCsvFeed } from '../src/lib/meta/feed';
import { normalizeUserData, generateEventId } from '../src/lib/meta/capi';
import { queueProducer } from '../src/lib/queue/producer';
import { CatalogProductRecord } from '../src/types';
import crypto from 'crypto';
import { verifyShopifySessionToken, createMockShopifySessionToken } from '../src/lib/auth/sessionToken';

let passed = 0;
let failed = 0;

function assert(condition: unknown, testName: string) {
  if (Boolean(condition)) {
    console.log(`  ✓ ${testName}`);
    passed++;
  } else {
    console.error(`  ✕ ${testName}`);
    failed++;
  }
}

async function runTests() {
  console.log('\n======================================================');
  console.log('🧪 RUNNING PRODUCTION TEST SUITE (SHOPIFY META CONNECTOR)');
  console.log('======================================================\n');

  // 1. CRYPTO & ENCRYPTION TESTS
  console.log('📦 1. Security & AES-256-GCM Encryption Tests:');
  const secret = 'shpat_live_access_token_secret_12345';
  const encrypted = encrypt(secret);
  assert(encrypted.split(':').length === 3, 'Encrypted output contains iv:authTag:ciphertext');
  assert(encrypted !== secret, 'Ciphertext differs from plaintext');
  const decrypted = decrypt(encrypted);
  assert(decrypted === secret, 'Decryption recovers exact original plaintext');

  try {
    const parts = encrypted.split(':');
    // Flip characters in auth tag to simulate tampering
    parts[1] = parts[1].replace(/^[0-9a-f]/i, (c) => (c === '0' ? '1' : '0'));
    const tampered = parts.join(':');
    decrypt(tampered);
    assert(false, 'Tampered ciphertext throws authentication tag error');
  } catch {
    assert(true, 'Tampered ciphertext throws authentication tag error');
  }

  // 2. SHOPIFY HMAC & DOMAIN VALIDATION
  console.log('\n📦 2. Shopify Webhook HMAC & OAuth Validation:');
  const testSecret = 'shopify_secret_key_testing_123';
  const rawBody = '{"id": 123456, "topic": "products/update"}';
  const validHmac = crypto.createHmac('sha256', testSecret).update(rawBody).digest('base64');
  assert(verifyShopifyWebhookHmac(rawBody, validHmac, testSecret), 'Valid Shopify Webhook HMAC passes timingSafeEqual');
  assert(!verifyShopifyWebhookHmac(rawBody, 'invalid_hmac', testSecret), 'Invalid Shopify Webhook HMAC fails');
  assert(isValidShopifyDomain('my-store.myshopify.com'), 'Valid myshopify domain accepted');
  assert(!isValidShopifyDomain('evil.com'), 'Invalid domain rejected');
  assert(!isValidShopifyDomain('my-store.myshopify.com.evil.com'), 'Subdomain spoofing rejected');

  // 3. META SHA-256 NORMALIZATION
  console.log('\n📦 3. Meta Conversions API (CAPI) SHA-256 Hashing:');
  const rawEmail = '  John.DOE@Example.com ';
  const expectedEmailHash = crypto.createHash('sha256').update('john.doe@example.com').digest('hex');
  assert(hashMetaField(rawEmail) === expectedEmailHash, 'Email normalized (trim + lowercase) and SHA-256 hashed');
  
  // Idempotency: Already hashed strings shouldn't be double hashed
  assert(hashMetaField(expectedEmailHash) === expectedEmailHash, 'Already hashed 64-char string is preserved without double-hashing');

  const rawPhone = '+1 (555) 019-2834';
  const expectedPhoneHash = crypto.createHash('sha256').update('15550192834').digest('hex');
  assert(hashMetaPhone(rawPhone) === expectedPhoneHash, 'Phone number stripped of symbols and SHA-256 hashed');

  const rawCountry = '  US ';
  assert(hashMetaCountry(rawCountry) === crypto.createHash('sha256').update('us').digest('hex'), 'Country code normalized to 2-letter lowercase and hashed');

  // 4. META XML & CSV FEED GENERATION
  console.log('\n📦 4. Meta Catalog Feed Generation (Variant-Level):');
  const sampleProducts: CatalogProductRecord[] = [
    {
      id: 'prod_1_v1',
      shop_id: 'shop_demo_123',
      shopify_product_id: 'gid://shopify/Product/1001',
      shopify_variant_id: 'gid://shopify/ProductVariant/2001',
      title: 'Organic Cotton Shirt',
      variant_title: 'Black / M',
      description: '100% organic cotton shirt',
      price: 29.99,
      compare_at_price: 39.99,
      currency: 'USD',
      availability: 'in stock',
      inventory_quantity: 10,
      image_url: 'https://example.com/image.jpg',
      product_url: 'https://example.com/products/shirt?variant=2001',
      brand: 'Modern Apparel',
      condition: 'new',
      google_product_category: 'Apparel & Accessories > Shirts',
      status: 'synced',
      last_synced_at: new Date().toISOString(),
    }
  ];

  const xmlFeed = generateMetaXmlFeed(sampleProducts, 'digi-horizon.myshopify.com');
  assert(xmlFeed.includes('<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0">'), 'XML contains RSS 2.0 header');
  assert(xmlFeed.includes('<g:id>shopify_US_1001_2001</g:id>'), 'XML contains variant-level itemId');
  assert(xmlFeed.includes('<g:item_group_id>shopify_US_1001</g:item_group_id>'), 'XML contains item_group_id for variant grouping');
  assert(xmlFeed.includes('<g:price>39.99 USD</g:price>'), 'XML contains formatted price and currency');
  assert(xmlFeed.includes('<g:sale_price>29.99 USD</g:sale_price>'), 'XML contains sale_price when compare_at_price exists');

  const csvFeed = generateMetaCsvFeed(sampleProducts);
  assert(csvFeed.includes('id,item_group_id,title,description'), 'CSV contains standard Meta headers');
  assert(csvFeed.includes('"shopify_US_1001_2001"'), 'CSV contains variant-level row');

  // 5. CAPI DEDUPLICATION & USER NORMALIZATION
  console.log('\n📦 5. CAPI Event Deduplication & Normalization:');
  const normUser = normalizeUserData({
    email: 'merchant@store.com',
    phone: '1234567890',
    clientIp: '1.2.3.4',
  });
  assert(Array.isArray(normUser.em) && normUser.em[0].length === 64, 'User email hashed in array');
  assert(normUser.client_ip_address === '1.2.3.4', 'Client IP preserved unhashed');

  const eventId1 = generateEventId('purchase', 'gid://shopify/Order/892019');
  const eventId2 = generateEventId('purchase', 'gid://shopify/Order/892019');
  assert(eventId1 === eventId2 && eventId1 === 'purchase_892019', 'Deterministic event_id generated for Shopify Order');

  // 6. ASYNC JOB QUEUE
  console.log('\n📦 6. Asynchronous Background Job Queue:');
  const job = await queueProducer.enqueue('catalog_sync', 'shop_digi_horizon', {
    sync_id: 'sync_test_1',
    shop_domain: 'quickstart-1a79b82e.myshopify.com',
  });
  assert(job.status === 'queued' || job.status === 'running' || job.status === 'completed', 'Job enqueued successfully');
  assert(job.shop_id === 'shop_digi_horizon', 'Job correctly associated with shop');

  // 7. PRICING PLANS & ADMIN CUSTOMIZER TESTS
  console.log('\n📦 7. Pricing Plans, Quota Limits & Admin Customizer:');
  const { db } = await import('../src/db/client');
  const { checkPlanLimits } = await import('../src/lib/shopify/billing');

  const allPlans = await db.getPlans();
  assert(allPlans.length >= 3, 'Found at least 3 standard plans (Basic, Growth, Accelerate)');

  const basic = await db.getPlan('basic');
  assert(basic !== null && basic.price === 0 && basic.sku_limit === 100, 'Basic Plan is Free with 100 SKU limit');

  const growth = await db.getPlan('growth');
  assert(growth !== null && growth.price === 13 && growth.sku_limit === 10000, 'Growth Plan is $13/mo with 10,000 SKU limit');

  const accelerate = await db.getPlan('accelerate');
  assert(accelerate !== null && accelerate.price === 99 && accelerate.sku_limit === null, 'Accelerate Plan is $99/mo with Unlimited SKUs');

  // Admin dynamic plan customization test
  const customUpdated = await db.updatePlan('growth', { price: 15, custom_titles: true });
  assert(customUpdated?.price === 15, 'Admin can customize plan price dynamically');

  // Reset back to factory defaults
  const resetPlans = await db.resetDefaultPlans();
  const resetGrowth = resetPlans.find((p) => p.id === 'growth');
  assert(resetGrowth?.price === 13, 'Admin can reset plans to factory defaults ($13/mo)');

  // Plan limits enforcer tests
  const limitCheckAllowed = await checkPlanLimits('shop_digi_horizon', 'upload_skus', 5);
  assert(limitCheckAllowed.allowed === true, 'Store with available quota passes upload check');

  // 8. MULTI-STORE STRICT TENANT ISOLATION TESTS
  console.log('\n📦 8. Strict Multi-Store Tenant Isolation & Cross-Store Protection:');
  const nowTime = new Date().toISOString();

  // Create Store Alpha & Store Beta
  await db.upsertShop({
    id: 'shop_store_alpha',
    shopify_domain: 'alpha-store.myshopify.com',
    access_token_encrypted: encrypt('shpat_alpha_token_123'),
    scopes: ['read_products'],
    installed_at: nowTime,
    status: 'installed',
    plan: 'growth',
    created_at: nowTime,
    updated_at: nowTime,
  });

  await db.upsertShop({
    id: 'shop_store_beta',
    shopify_domain: 'beta-store.myshopify.com',
    access_token_encrypted: encrypt('shpat_beta_token_456'),
    scopes: ['read_products'],
    installed_at: nowTime,
    status: 'installed',
    plan: 'basic',
    created_at: nowTime,
    updated_at: nowTime,
  });

  // Add distinct products
  await db.upsertProduct({
    id: 'prod_alpha_test_01',
    shop_id: 'shop_store_alpha',
    shopify_product_id: 'gid://shopify/Product/101',
    shopify_variant_id: 'gid://shopify/ProductVariant/1011',
    title: 'Store Alpha Silk Shirt',
    price: 89.00,
    currency: 'USD',
    availability: 'in stock',
    inventory_quantity: 50,
    image_url: 'https://images.example.com/alpha.jpg',
    product_url: 'https://alpha-store.myshopify.com/products/shirt',
    brand: 'Alpha Brand',
    condition: 'new',
    description: 'Alpha store exclusive',
    status: 'synced',
    last_synced_at: nowTime,
  });

  await db.upsertProduct({
    id: 'prod_beta_test_01',
    shop_id: 'shop_store_beta',
    shopify_product_id: 'gid://shopify/Product/201',
    shopify_variant_id: 'gid://shopify/ProductVariant/2011',
    title: 'Store Beta Leather Boots',
    price: 150.00,
    currency: 'USD',
    availability: 'in stock',
    inventory_quantity: 20,
    image_url: 'https://images.example.com/beta.jpg',
    product_url: 'https://beta-store.myshopify.com/products/boots',
    brand: 'Beta Brand',
    condition: 'new',
    description: 'Beta store exclusive',
    status: 'synced',
    last_synced_at: nowTime,
  });

  // Add CAPI events
  await db.recordCapiEvent({
    id: 'capi_alpha_test_01',
    shop_id: 'shop_store_alpha',
    event_id: 'evt_alpha_001',
    event_name: 'Purchase',
    payload_hash: 'hash_alpha_123',
    user_data_masked: { em: 'a***@alpha.com' },
    status: 'sent',
    response_code: 200,
    duration_ms: 45,
    created_at: nowTime,
  });

  await db.recordCapiEvent({
    id: 'capi_beta_test_01',
    shop_id: 'shop_store_beta',
    event_id: 'evt_beta_001',
    event_name: 'AddToCart',
    payload_hash: 'hash_beta_456',
    user_data_masked: { em: 'b***@beta.com' },
    status: 'sent',
    response_code: 200,
    duration_ms: 60,
    created_at: nowTime,
  });

  // Verify Store Alpha sees only Store Alpha
  const alphaProducts = await db.getProductsByShopId('shop_store_alpha');
  assert(
    alphaProducts.length === 1 && alphaProducts[0].title === 'Store Alpha Silk Shirt',
    'Store Alpha queries return strictly Store Alpha products'
  );
  assert(
    !alphaProducts.some((p) => p.shop_id === 'shop_store_beta'),
    'Store Beta products are completely hidden and blocked from Store Alpha'
  );

  // Verify Store Beta sees only Store Beta
  const betaProducts = await db.getProductsByShopId('shop_store_beta');
  assert(
    betaProducts.length === 1 && betaProducts[0].title === 'Store Beta Leather Boots',
    'Store Beta queries return strictly Store Beta products'
  );
  assert(
    !betaProducts.some((p) => p.shop_id === 'shop_store_alpha'),
    'Store Alpha products are completely hidden and blocked from Store Beta'
  );

  // Verify Dashboard Metrics per store
  const alphaMetrics = await db.getStoreDashboardMetrics('shop_store_alpha');
  assert(
    alphaMetrics.products.total === 1 && alphaMetrics.products.catalogValue === 4450 && alphaMetrics.capi.total === 1,
    'Store Alpha dashboard metrics are isolated and computed exclusively for Alpha'
  );

  const betaMetrics = await db.getStoreDashboardMetrics('shop_store_beta');
  assert(
    betaMetrics.products.total === 1 && betaMetrics.products.catalogValue === 3000 && betaMetrics.capi.total === 1,
    'Store Beta dashboard metrics are isolated and computed exclusively for Beta'
  );
  // 9. SHOPIFY APP BRIDGE SESSION TOKEN SECURITY & AUTHENTICATION
  console.log('\n📦 9. Shopify App Bridge Session Token Security (Built for Shopify Requirements):');
  const testApiKey = 'fcf3c95da56ecc483ade6dabbdb49e92';
  const sessionSecret = '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';

  // 9a. Valid Session Token
  const validToken = createMockShopifySessionToken('test-store.myshopify.com', {
    secret: sessionSecret,
    apiKey: testApiKey,
    expiresInSeconds: 300,
    sub: 'user_456',
  });
  const validRes = verifyShopifySessionToken(validToken, { secret: sessionSecret, apiKey: testApiKey });
  assert(validRes.valid === true && validRes.data?.shopDomain === 'test-store.myshopify.com', 'Cryptographically valid session token accepted');
  assert(validRes.data?.userId === 'user_456', 'Session token user ID sub claim correctly extracted');

  // 9b. Tampered Signature
  const tokenParts = validToken.split('.');
  const forgedSig = Buffer.from('forged_invalid_signature_bytes_here_12').toString('base64url');
  const tamperedToken = `${tokenParts[0]}.${tokenParts[1]}.${forgedSig}`;
  const tamperedRes = verifyShopifySessionToken(tamperedToken, { secret: sessionSecret, apiKey: testApiKey });
  assert(tamperedRes.valid === false && tamperedRes.error === 'Invalid session token signature', 'Forged session token signature rejected');

  // 9c. Expired Session Token
  const expiredToken = createMockShopifySessionToken('test-store.myshopify.com', {
    secret: sessionSecret,
    apiKey: testApiKey,
    expiresInSeconds: -60, // expired 60 seconds ago
  });
  const expiredRes = verifyShopifySessionToken(expiredToken, { secret: sessionSecret, apiKey: testApiKey });
  assert(expiredRes.valid === false && expiredRes.error === 'Session token has expired', 'Expired session token rejected');

  // 9d. Audience Mismatch
  const wrongAudToken = createMockShopifySessionToken('test-store.myshopify.com', {
    secret: sessionSecret,
    apiKey: 'wrong_api_key_aud_value',
    expiresInSeconds: 300,
  });
  const audRes = verifyShopifySessionToken(wrongAudToken, { secret: sessionSecret, apiKey: testApiKey });
  assert(Boolean(audRes.valid === false && audRes.error?.includes('Audience mismatch')), 'Audience mismatch token rejected');

  // 9e. Malformed Token
  const malformedRes = verifyShopifySessionToken('not.a.valid.jwt.string', { secret: sessionSecret, apiKey: testApiKey });
  assert(malformedRes.valid === false, 'Malformed token string rejected');

  // 10. SAAS ADMIN DASHBOARD & MULTI-STORE MANAGEMENT
  console.log('\n📦 10. SaaS Admin Dashboard, Multi-Store Analytics, Verified Billing & RBAC:');
  const { signAdminToken, verifyAdminToken, hashPassword, verifyPassword, checkAdminPermission } = await import('../src/lib/auth/adminAuth');
  const { REALISTIC_DEMO_STORES } = await import('../src/lib/saas-admin/demoData');

  // 10a. Admin Auth & RBAC
  const { hash, salt } = hashPassword('super_admin_pass');
  assert(verifyPassword('super_admin_pass', hash, salt), 'Admin PBKDF2 password verification succeeds');
  assert(!verifyPassword('wrong_pass', hash, salt), 'Admin PBKDF2 wrong password fails');

  const adminToken = signAdminToken({
    id: 'adm_runner_01',
    email: 'admin@meta-connector.app',
    name: 'Sarah Jenkins',
    role: 'super_admin',
  });
  const verifiedAdmin = verifyAdminToken(adminToken);
  assert(verifiedAdmin.valid === true, 'Admin HMAC-SHA256 token verified');
  assert(checkAdminPermission('super_admin', 'manage_stores') === true, 'Super Admin has manage_stores permission');
  assert(checkAdminPermission('support_agent', 'manage_stores') === false, 'Support Agent denied manage_stores permission');

  // 10b. Verified Billing Logic (Free stores != Paid)
  const allDbStores = await db.getAllShops();
  const freeStores = allDbStores.filter((s) => s.plan === 'basic' || s.subscription_status === 'free');
  assert(freeStores.every((s) => (s.monthly_revenue || 0) === 0), 'Free stores are never counted as paid subscribers');

  const paidStores = allDbStores.filter((s) => s.status === 'installed' && s.subscription_status === 'active' && (s.monthly_revenue || 0) > 0);
  const totalMRR = paidStores.reduce((sum, s) => sum + (s.monthly_revenue || 0), 0);
  assert(totalMRR >= 0, 'MRR computed strictly from active verified paid subscriptions');

  // 10c. Multi-Store Search & Details
  const defaultShop = await db.getDefaultShop();
  assert(Boolean(defaultShop && defaultShop.shopify_domain), 'Store search and deep statistics resolution verified');

  // 10d. Database SaaS Admin Tables & Persistence
  const adminDb = await db.getAdminUserByEmail('admin@meta-connector.app');
  assert(Boolean(adminDb && adminDb.role === 'super_admin'), 'Root SaaS Administrator persisted in database');

  // 11. CONVERSECART REFERENCE BILLING ENGINE TESTS
  console.log('\n📦 11. ConverseCart Reference Billing Engine & Shopify GraphQL Billing:');
  const { BillingService, canUseFeature, assertCanUseFeature, FeatureAccessError } = await import('../src/lib/shopify/billing');

  // 11a. Feature Capability
  assert(canUseFeature('basic', 'meta.capi_tracking') === false, 'Basic tier denied CAPI tracking');
  assert(canUseFeature('growth', 'meta.capi_tracking') === true, 'Growth tier granted CAPI tracking');
  assert(canUseFeature('accelerate', 'meta.catalog_unlimited') === true, 'Accelerate tier granted unlimited catalog');

  let accessErrorCaught = false;
  try {
    assertCanUseFeature('basic', 'meta.capi_tracking');
  } catch (e: any) {
    accessErrorCaught = e instanceof FeatureAccessError && e.statusCode === 403;
  }
  assert(accessErrorCaught, 'assertCanUseFeature enforces HTTP 403 FeatureAccessError');

  // 11b. Plan Subscriptions (Free vs Paid)
  const billingShopId = 'shop_runner_billing_' + Date.now();
  const billingDomain = `runner-billing-${Date.now()}.myshopify.com`;
  const billingNow = new Date().toISOString();
  await db.upsertShop({
    id: billingShopId,
    shopify_domain: billingDomain,
    access_token_encrypted: 'mock_token',
    scopes: ['read_products'],
    installed_at: billingNow,
    created_at: billingNow,
    updated_at: billingNow,
    status: 'installed',
    plan: 'growth',
    monthly_revenue: 13,
  });

  const freeSub = await BillingService.createSubscription({
    shopDomainOrId: billingShopId,
    plan: 'basic',
  });
  assert(freeSub.status === 'active' && freeSub.price === 0, 'Basic free plan activated with $0 charge');

  const paidSub = await BillingService.createSubscription({
    shopDomainOrId: billingShopId,
    plan: 'accelerate',
  });
  assert(Boolean(paidSub.confirmationUrl && paidSub.price === 99), 'Accelerate plan generates $99 confirmationUrl');

  // 11c. Authoritative Sync & Cancellation
  const syncedPlan = await BillingService.syncSubscription(billingShopId);
  assert(syncedPlan.plan === 'basic' && syncedPlan.status === 'free', 'Authoritative sync resets store to Basic without active charge');

  await BillingService.cancelSubscription(billingShopId);
  const cancelledShop = await db.getShopById(billingShopId);
  assert(cancelledShop?.plan === 'basic' && cancelledShop?.subscription_status === 'free', 'Subscription cancellation reverts to Basic Plan');

  // Cleanup fixtures
  await db.deleteProduct('prod_alpha_test_01');
  await db.deleteProduct('prod_beta_test_01');

  console.log('\n======================================================');
  console.log(`🎉 TEST SUITE COMPLETED: ${passed} PASSED, ${failed} FAILED`);
  console.log('======================================================\n');

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

runTests().catch((err) => {
  console.error('Test runner fatal error:', err);
  process.exit(1);
});
